@semacode/cli 3.9.3 → 3.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin.js CHANGED
@@ -105,6 +105,16 @@ export async function executarCliPublica(argv = process.argv.slice(2)) {
105
105
  if (!execucao) {
106
106
  return emitirFalhaControle(argv, "FATAL_ERROR", "CLI_FATAL_ERROR", "Falha ao executar a CLI da Sema.", 1);
107
107
  }
108
+ try {
109
+ const doc = JSON.parse(execucao.stdout.trim());
110
+ if (doc.schemaVersion === "sema.cli.control/v1" && typeof doc.kind === "string" && doc.exitCode === execucao.codigoSaida) {
111
+ console.log(execucao.stdout);
112
+ return execucao.codigoSaida;
113
+ }
114
+ }
115
+ catch {
116
+ // nao e um envelope de controle; segue o caminho normal
117
+ }
108
118
  return emitirResultadoCliJsonV1({
109
119
  comando: sintaxe.comando,
110
120
  codigoSaida: execucao.codigoSaida,
@@ -1,12 +1,17 @@
1
- export type CategoriaFalhaControleCli = "UNKNOWN_COMMAND" | "ARGUMENT_ERROR" | "FATAL_ERROR";
1
+ export type CategoriaFalhaControleCli = "UNKNOWN_COMMAND" | "ARGUMENT_ERROR" | "CONTRACT_NOT_FOUND" | "FATAL_ERROR";
2
2
  export declare class CliControlError extends Error {
3
3
  readonly categoria: CategoriaFalhaControleCli;
4
4
  readonly codigoPublico: string;
5
5
  readonly mensagemPublica: string;
6
6
  readonly codigoSaida: number;
7
- constructor(categoria: CategoriaFalhaControleCli);
7
+ readonly detalhes?: Record<string, unknown>;
8
+ constructor(categoria: CategoriaFalhaControleCli, detalhes?: Record<string, unknown>);
8
9
  }
9
10
  export declare function erroComandoDesconhecido(): CliControlError;
10
11
  export declare function erroArgumentoInvalido(): CliControlError;
12
+ export declare function erroContratoNaoEncontrado(detalhes: {
13
+ caminhoTentado: string;
14
+ sugeridos: string[];
15
+ }): CliControlError;
11
16
  export declare function erroFatalCli(): CliControlError;
12
17
  export declare function ehCliControlError(valor: unknown): valor is CliControlError;
@@ -1,5 +1,6 @@
1
1
  // SEMA-GOVERNED: sema.produto.cli_invocacao_publica.argumentos
2
2
  // Descrição: representa falhas públicas de controle sem carregar argv, stack ou caminhos na mensagem.
3
+ const MENSAGEM_CONTRATO_NAO_ENCONTRADO = "Contrato Sema nao encontrado. Consulte os contratos sugeridos no envelope.";
3
4
  const DEFINICOES_FALHA_CONTROLE = Object.freeze({
4
5
  UNKNOWN_COMMAND: Object.freeze({
5
6
  codigoPublico: "CLI_UNKNOWN_COMMAND",
@@ -11,6 +12,11 @@ const DEFINICOES_FALHA_CONTROLE = Object.freeze({
11
12
  mensagemPublica: "Argumentos inválidos. Consulte a ajuda do comando.",
12
13
  codigoSaida: 1,
13
14
  }),
15
+ CONTRACT_NOT_FOUND: Object.freeze({
16
+ codigoPublico: "CLI_CONTRACT_NOT_FOUND",
17
+ mensagemPublica: MENSAGEM_CONTRATO_NAO_ENCONTRADO,
18
+ codigoSaida: 2,
19
+ }),
14
20
  FATAL_ERROR: Object.freeze({
15
21
  codigoPublico: "CLI_FATAL_ERROR",
16
22
  mensagemPublica: "Falha ao executar a CLI da Sema.",
@@ -22,7 +28,8 @@ export class CliControlError extends Error {
22
28
  codigoPublico;
23
29
  mensagemPublica;
24
30
  codigoSaida;
25
- constructor(categoria) {
31
+ detalhes;
32
+ constructor(categoria, detalhes) {
26
33
  const definicao = DEFINICOES_FALHA_CONTROLE[categoria];
27
34
  super(definicao.mensagemPublica);
28
35
  this.name = "CliControlError";
@@ -30,6 +37,7 @@ export class CliControlError extends Error {
30
37
  this.codigoPublico = definicao.codigoPublico;
31
38
  this.mensagemPublica = definicao.mensagemPublica;
32
39
  this.codigoSaida = definicao.codigoSaida;
40
+ this.detalhes = detalhes;
33
41
  }
34
42
  }
35
43
  export function erroComandoDesconhecido() {
@@ -38,6 +46,9 @@ export function erroComandoDesconhecido() {
38
46
  export function erroArgumentoInvalido() {
39
47
  return new CliControlError("ARGUMENT_ERROR");
40
48
  }
49
+ export function erroContratoNaoEncontrado(detalhes) {
50
+ return new CliControlError("CONTRACT_NOT_FOUND", detalhes);
51
+ }
41
52
  export function erroFatalCli() {
42
53
  return new CliControlError("FATAL_ERROR");
43
54
  }
@@ -10,7 +10,7 @@ const COMANDOS_PUBLICOS = new Set([
10
10
  "impacto", "importar", "iniciar", "init", "inspecionar", "instalar-exemplos",
11
11
  "interativo", "ir", "pipeline", "profile", "prompt-curto", "prompt-ia",
12
12
  "prompt-ia-react", "prompt-ia-sema-primeiro", "prompt-ia-ui", "renomear-semantico",
13
- "resumo", "rule-packs", "skill", "starter-ia", "sync", "sync-codex", "testar",
13
+ "resumo", "rule-packs", "sessao", "skill", "starter-ia", "sync", "sync-codex", "testar",
14
14
  "validar", "verificar", "ast",
15
15
  ]);
16
16
  const TEMPLATES_INICIAR = new Set([
@@ -451,6 +451,7 @@ function validarComandoBasico(comando, args) {
451
451
  return Boolean(alvo && ALVOS_GERACAO.has(alvo)) && (!estrutura || ESTRUTURAS.has(estrutura)) && (!framework || FRAMEWORKS.has(framework));
452
452
  }
453
453
  case "verificar": return parsearArgumentos(args, { opcoes: ["--saida", "--alvo"], flags: ["--sem-cache"], maxPosicionais: 1 }) !== null;
454
+ case "sessao": return parsearArgumentos(args, { flags: ["--json"], maxPosicionais: 2 }) !== null;
454
455
  case "formatar": return parsearArgumentos(args, { flags: ["--check"], maxPosicionais: 1 }) !== null;
455
456
  case "inspecionar": {
456
457
  const parsed = parsearArgumentos(args, { opcoes: ["--drift", "--cache"], flags: ["--com-drift"], maxPosicionais: 1 });
@@ -143,6 +143,7 @@ export function criarAjudaRaiz(versao) {
143
143
  "sema contexto-ia <arquivo.sema> [--saida <diretorio>] [--json]",
144
144
  "sema sync-codex [--json]",
145
145
  "sema instalar-exemplos [--json]",
146
+ "sema sessao [intencao] [entrada] [--json]",
146
147
  "sema docs-impacto --intencao <acao> [--arquivo <caminho>] [--criar-ausentes] [--completo] [--json]",
147
148
  "sema finalizar-mudanca --intencao <acao> [--arquivo <caminho>] [--doc-lida <caminho>] [--json]",
148
149
  "sema author briefing <arquivo.sema> [--json]",
@@ -12,7 +12,21 @@ export async function executarCliRuntimeInterno(principalInjetado) {
12
12
  ? codigoSaida
13
13
  : CODIGO_SAIDA_FATAL_RUNTIME_CLI;
14
14
  }
15
- catch {
15
+ catch (erro) {
16
+ if (typeof erro?.categoria === "string" && erro.categoria) {
17
+ const cli = erro;
18
+ const envelope = {
19
+ schemaVersion: "sema.cli.control/v1",
20
+ ok: false,
21
+ kind: cli.categoria,
22
+ code: cli.codigoPublico,
23
+ message: cli.mensagemPublica,
24
+ exitCode: cli.codigoSaida,
25
+ ...(cli.detalhes ? { details: cli.detalhes } : {}),
26
+ };
27
+ console.log(JSON.stringify(envelope, null, 2));
28
+ return cli.codigoSaida;
29
+ }
16
30
  return CODIGO_SAIDA_FATAL_RUNTIME_CLI;
17
31
  }
18
32
  }
@@ -0,0 +1,34 @@
1
+ export interface SessaoEnvelope {
2
+ comando: "sessao";
3
+ sucesso: boolean;
4
+ workspace: {
5
+ baseProjeto: string;
6
+ nomePasta: string;
7
+ hashContratos: string;
8
+ cliVersao: string;
9
+ };
10
+ frescor: {
11
+ fresco: boolean;
12
+ hashArtefato: string | null;
13
+ hashAtual: string;
14
+ sugestao: string;
15
+ };
16
+ contratos: {
17
+ total: number;
18
+ modulos: string[];
19
+ };
20
+ intencoes: {
21
+ intencao: string;
22
+ categorias: string[];
23
+ docsBloqueantes: string[];
24
+ docsRecomendadas: string[];
25
+ };
26
+ gates: {
27
+ drift: string;
28
+ validar: string;
29
+ verificar: string;
30
+ finalizar: string;
31
+ };
32
+ proximosPassos: string[];
33
+ }
34
+ export declare function comandoSessao(posicionais: string[], args: string[], emJson: boolean, cwd?: string): Promise<number>;
@@ -0,0 +1,86 @@
1
+ // SEMA-GOVERNED: sema.produto.cli_sessao
2
+ // Descricao: comando sessao devolve envelope compacto de abertura governada.
3
+ import path from "node:path";
4
+ import { verificarFrescorArtefatos } from "./index.part04.js";
5
+ import { carregarProjeto } from "./projetoCarregar.js";
6
+ import { resolverDocumentacaoObrigatoria } from "./docs.js";
7
+ export async function comandoSessao(posicionais, args, emJson, cwd = process.cwd()) {
8
+ const intencao = posicionais[0] ?? args.find((arg) => arg.startsWith("--intencao="))?.split("=")[1] ?? "";
9
+ const entrada = posicionais[1];
10
+ const baseProjeto = entrada ? path.resolve(cwd, entrada) : cwd;
11
+ const contextoProjeto = await carregarProjeto(entrada, cwd, {
12
+ escopo: "projeto",
13
+ adiarDescobertaCodigo: true,
14
+ });
15
+ const pacoteCli = (await import("../package.json", { with: { type: "json" } })).default;
16
+ const frescor = await verificarFrescorArtefatos(contextoProjeto.baseProjeto);
17
+ const modulosNomes = contextoProjeto.modulosSelecionados.map((item) => item.resultado.modulo?.nome ?? path.basename(item.caminho, ".sema"));
18
+ let categorias = [];
19
+ let docsBloqueantes = [];
20
+ let docsRecomendadas = [];
21
+ if (intencao.trim()) {
22
+ const impacto = await resolverDocumentacaoObrigatoria({
23
+ baseProjeto: contextoProjeto.baseProjeto,
24
+ intencao,
25
+ arquivosAlvo: [],
26
+ criarAusentes: false,
27
+ });
28
+ categorias = impacto.categorias;
29
+ docsBloqueantes = impacto.leituraObrigatoria
30
+ .filter((doc) => doc.obrigatoriedade !== "recomendada" && doc.existe)
31
+ .map((doc) => doc.relativo);
32
+ docsRecomendadas = impacto.leituraRecomendada.map((doc) => doc.relativo);
33
+ }
34
+ const envelope = {
35
+ comando: "sessao",
36
+ sucesso: true,
37
+ workspace: {
38
+ baseProjeto: contextoProjeto.baseProjeto,
39
+ nomePasta: path.basename(contextoProjeto.baseProjeto),
40
+ hashContratos: frescor.hashAtual,
41
+ cliVersao: pacoteCli.version,
42
+ },
43
+ frescor,
44
+ contratos: {
45
+ total: contextoProjeto.modulosSelecionados.length,
46
+ modulos: modulosNomes.slice(0, 10),
47
+ },
48
+ intencoes: {
49
+ intencao,
50
+ categorias,
51
+ docsBloqueantes,
52
+ docsRecomendadas,
53
+ },
54
+ gates: {
55
+ drift: "sema drift . --escopo projeto --cache fresh --json",
56
+ validar: "sema validar contratos --json",
57
+ verificar: "sema verificar contratos --alvo typescript --json",
58
+ finalizar: 'sema finalizar-mudanca --intencao "<intencao>" --doc-lida <doc> --json',
59
+ },
60
+ proximosPassos: [
61
+ ...(!frescor.fresco ? ["Artefatos stale detectados — rode sema sync-codex --json antes de agir"] : []),
62
+ ...(docsBloqueantes.length > 0 ? [`Leia antes de agir: ${docsBloqueantes.slice(0, 5).join(", ")}`] : []),
63
+ ...(intencao.trim() ? ["Siga o protocolo AGENTS.md passo a passo"] : ["Declare --intencao para receber gates especificos"]),
64
+ ],
65
+ };
66
+ if (emJson) {
67
+ console.log(JSON.stringify(envelope, null, 2));
68
+ return 0;
69
+ }
70
+ console.log(`Sessao Sema — ${envelope.workspace.nomePasta}`);
71
+ console.log(` Hash contratos: ${envelope.workspace.hashContratos}`);
72
+ console.log(` CLI: ${envelope.workspace.cliVersao}`);
73
+ console.log(` Frescor: ${frescor.fresco ? "fresco" : "STALE"} ${!frescor.fresco ? `(${frescor.sugestao})` : ""}`);
74
+ console.log(` Contratos: ${envelope.contratos.total}`);
75
+ if (intencao) {
76
+ console.log(` Intencao: ${intencao}`);
77
+ console.log(` Categorias: ${categorias.join(", ")}`);
78
+ console.log(` Docs bloqueantes: ${docsBloqueantes.length}`);
79
+ console.log(` Docs recomendadas: ${docsRecomendadas.length}`);
80
+ }
81
+ for (const passo of envelope.proximosPassos) {
82
+ console.log(` → ${passo}`);
83
+ }
84
+ return 0;
85
+ }
86
+ //# sourceMappingURL=comandoSessao.js.map
@@ -128,7 +128,7 @@ export async function listarArquivosRecursivo(base, limite = 80) {
128
128
  export const REGRAS_DOCUMENTACAO = [
129
129
  {
130
130
  categoria: "deploy",
131
- padroes: [/deploy/, /publicar/, /producao/, /release/, /pipeline/, /\bci\b/, /\bcd\b/, /vercel/, /cloudflare/, /docker/],
131
+ padroes: [/deploy/, /publicar/, /producao/, /release/, /pipeline/, /\bci\b/, /\bcd\b/, /vercel/, /cloudflare/, /docker/, /edge.?function/i, /\bruntime\b/i, /\blambda\b/i, /\bworker\b/i, /\bserverless\b/i],
132
132
  docs: [
133
133
  { relativo: "docs/deploy.md", tipo: "runbook", motivo: "runbook de deploy antes de publicar ou alterar pipeline", permitirCriacao: true },
134
134
  { relativo: "docs/env.md", tipo: "runbook", motivo: "variaveis, secrets e ambientes usados no deploy", permitirCriacao: true },
@@ -4,7 +4,7 @@ import { type GuiaCapacidadeIaMap } from './agentContextTipos.js';
4
4
  import { renderizarCaixaAscii, renderizarSecaoAscii } from './cliHelpTexto.js';
5
5
  import { criarBriefingAgente, criarBriefingAgenteContratos, resumirDriftPorModulo } from "./index.part03.js";
6
6
  import { type ComandoConsultaDrift, type ModoCacheDrift, type ResolucaoModoCacheDrift } from "./driftCacheModes.js";
7
- export type Comando = "iniciar" | "init" | "dev" | "sync" | "author" | "profile" | "conteudo" | "interativo" | "descobrir" | "pipeline" | "capabilities" | "validar" | "ast" | "ir" | "compilar" | "gerar" | "testar" | "diagnosticos" | "verificar" | "inspecionar" | "drift" | "impacto" | "renomear-semantico" | "importar" | "doctor" | "formatar" | "ajuda-ia" | "starter-ia" | "sync-codex" | "skill" | "instalar-exemplos" | "rule-packs" | "docs-impacto" | "finalizar-mudanca" | "resumo" | "prompt-curto" | "prompt-ia" | "prompt-ia-ui" | "prompt-ia-react" | "prompt-ia-sema-primeiro" | "exemplos-prompt-ia" | "contexto-ia";
7
+ export type Comando = "iniciar" | "init" | "dev" | "sync" | "author" | "profile" | "conteudo" | "interativo" | "descobrir" | "pipeline" | "capabilities" | "validar" | "ast" | "ir" | "compilar" | "gerar" | "testar" | "diagnosticos" | "verificar" | "inspecionar" | "drift" | "impacto" | "renomear-semantico" | "importar" | "doctor" | "formatar" | "ajuda-ia" | "starter-ia" | "sync-codex" | "skill" | "instalar-exemplos" | "rule-packs" | "docs-impacto" | "finalizar-mudanca" | "resumo" | "sessao" | "prompt-curto" | "prompt-ia" | "prompt-ia-ui" | "prompt-ia-react" | "prompt-ia-sema-primeiro" | "exemplos-prompt-ia" | "contexto-ia";
8
8
  export interface ResultadoFormatacaoArquivo {
9
9
  caminho: string;
10
10
  alterado: boolean;
@@ -84,7 +84,7 @@ export function coletarResumoSemanticoModulo(contexto) {
84
84
  efeitos: limitarLista(efeitos, 8),
85
85
  erros: limitarLista(erros, 8),
86
86
  entidadesAfetadas: limitarLista(entidadesAfetadas, 8),
87
- arquivosProvaveis: limitarLista(unicosOrdenados(briefing.oQueTocar), 8),
87
+ arquivosProvaveis: limitarLista(unicosOrdenados(briefing.oQueTocar.map((item) => relativizarCaminho(item))), 8),
88
88
  simbolosRelacionados: limitarLista(unicosOrdenados(briefing.simbolosRelacionados), 8),
89
89
  riscosPrincipais: limitarLista(unicosOrdenados(briefing.riscosPrincipais), 6),
90
90
  lacunas: limitarLista(unicosOrdenados(briefing.oQueEstaFrouxo), 6),
@@ -96,7 +96,7 @@ export function coletarResumoSemanticoModulo(contexto) {
96
96
  consumerSurfaces: limitarLista(unicosOrdenados(briefing.consumerSurfaces ?? []), 8),
97
97
  consumerBridges: limitarLista(unicosOrdenados(briefing.consumerBridges ?? []), 8),
98
98
  ancoragensVinculo: limitarLista(unicosOrdenados(briefing.ancoragensVinculo ?? []), 8),
99
- arquivosProvaveisEditar: limitarLista(unicosOrdenados(briefing.arquivosProvaveisEditar ?? briefing.oQueTocar), 8),
99
+ arquivosProvaveisEditar: limitarLista(unicosOrdenados((briefing.arquivosProvaveisEditar ?? briefing.oQueTocar).map((item) => relativizarCaminho(item))), 8),
100
100
  };
101
101
  return modoVerificacaoCodigo === "codigo_completo"
102
102
  ? resumo
@@ -357,4 +357,10 @@ Contexto compacto:
357
357
  ${resumoTexto}
358
358
  `;
359
359
  }
360
+ function relativizarCaminho(caminho) {
361
+ const base = process.cwd();
362
+ const absoluto = path.resolve(base, caminho);
363
+ const relativo = path.relative(base, absoluto);
364
+ return relativo && !relativo.startsWith("..") ? relativo.replace(/[\\/]/g, "/") : caminho.replace(/[\\/]/g, "/");
365
+ }
360
366
  //# sourceMappingURL=index.part02.js.map
@@ -2,6 +2,13 @@ import { type GuiaCapacidadeIaMap } from './agentContextTipos.js';
2
2
  import { ContextoIaGerado, ResumoSemanticoModuloIa, type ResultadoDriftIa } from "./index.part01.js";
3
3
  import type { ResolucaoModoCacheDrift } from "./driftCacheModes.js";
4
4
  export declare const ARQUIVOS_RESUMO_PROJETO_IA: readonly ["SEMA_BOOT.md", "SEMA_SMALL_MODEL.md", "SEMA_BRIEF.md", "SEMA_BRIEF.micro.txt", "SEMA_BRIEF.curto.txt", "SEMA_INDEX.json", "AGENT_CONTEXT_PACK.json", "docs/ai-integration.md"];
5
+ export declare function verificarFrescorArtefatos(baseProjeto: string): Promise<{
6
+ fresco: boolean;
7
+ hashArtefato: string | null;
8
+ hashAtual: string;
9
+ contratosMudaram: boolean;
10
+ sugestao: string;
11
+ }>;
5
12
  export declare function gerarResumoProjetoIa(entrada: string | undefined, pastaSaidaOpcional?: string, escreverNaRaiz?: boolean, analiseDrift?: ResolucaoModoCacheDrift): Promise<{
6
13
  geradoEm: string;
7
14
  baseProjeto: string;
@@ -1,6 +1,7 @@
1
1
  // SEMA-GOVERNED: sema.governanca_ia_contexto, sema.produto.escrita_segura_workspace, sema.produto.governanca_ia.drift.cache.modos
2
2
  // Descrição: gera contexto local e grava artefatos somente dentro da base validada.
3
- import { mkdir } from "node:fs/promises";
3
+ import { mkdir, readFile } from "node:fs/promises";
4
+ import { createHash } from "node:crypto";
4
5
  import path from "node:path";
5
6
  import { formatarDiagnosticos, gerarRespostaValidacao, temErros, } from "@sema/nucleo";
6
7
  import { carregarProjeto, } from "./projeto.js";
@@ -53,6 +54,43 @@ function escolherTopArquivosResumo(modulos, arquivosCodigoDetectados) {
53
54
  }
54
55
  return { arquivos: [], fonte: "nenhum" };
55
56
  }
57
+ async function calcularHashContratos(caminhosContratos, baseProjeto) {
58
+ const hash = createHash("sha256");
59
+ const base = baseProjeto ?? (caminhosContratos[0] ? path.dirname(caminhosContratos[0]) : process.cwd());
60
+ const ordenados = [...caminhosContratos].sort();
61
+ for (const caminho of ordenados) {
62
+ const conteudo = await readFile(caminho, "utf8").catch(() => "");
63
+ const relativo = path.relative(base, caminho).replace(/[\\/]/g, "/");
64
+ hash.update(relativo);
65
+ hash.update(":");
66
+ hash.update(conteudo);
67
+ hash.update(String.fromCharCode(0));
68
+ }
69
+ return hash.digest("hex").slice(0, 16);
70
+ }
71
+ export async function verificarFrescorArtefatos(baseProjeto) {
72
+ const caminhoIndex = path.join(baseProjeto, "SEMA_INDEX.json");
73
+ let hashArtefato = null;
74
+ try {
75
+ const index = JSON.parse(await readFile(caminhoIndex, "utf8"));
76
+ hashArtefato = index.hashContratos ?? null;
77
+ }
78
+ catch {
79
+ hashArtefato = null;
80
+ }
81
+ const origens = await (await import("./projetoOrigens.js")).resolverOrigensProjeto(baseProjeto, baseProjeto, undefined);
82
+ const { listarArquivosDeOrigens } = await import("./projetoOrigens.js");
83
+ const contratos = await listarArquivosDeOrigens(origens);
84
+ const hashAtual = await calcularHashContratos(contratos, baseProjeto);
85
+ const fresco = hashArtefato === hashAtual;
86
+ return {
87
+ fresco,
88
+ hashArtefato,
89
+ hashAtual,
90
+ contratosMudaram: !fresco,
91
+ sugestao: fresco ? "" : "Artefatos de contexto estao desatualizados; rode sema sync-codex --json para regenerar.",
92
+ };
93
+ }
56
94
  export async function gerarResumoProjetoIa(entrada, pastaSaidaOpcional, escreverNaRaiz = false, analiseDrift = { modo: "none", executar: false, avisos: [] }) {
57
95
  const contextoProjeto = await carregarProjeto(entrada, process.cwd(), {
58
96
  escopo: "projeto",
@@ -121,10 +159,13 @@ export async function gerarResumoProjetoIa(entrada, pastaSaidaOpcional, escrever
121
159
  const semaBoot = renderizarSemaBoot(agentContextPack);
122
160
  const semaSmallModel = renderizarSemaSmallModel(agentContextPack);
123
161
  const docAgentesCapacidade = renderizarDocumentoAgentesPorCapacidade(agentContextPack);
162
+ const caminhosContratos = contextoProjeto.modulosSelecionados.map((item) => item.caminho);
163
+ const hashContratos = await calcularHashContratos(caminhosContratos, contextoProjeto.baseProjeto);
124
164
  const indexJson = {
125
165
  comando: "resumo-projeto",
126
166
  geradoEm,
127
167
  cliVersao: VERSAO_CLI,
168
+ hashContratos,
128
169
  baseProjeto,
129
170
  modoVerificacaoCodigo,
130
171
  analiseDrift: {
@@ -142,7 +183,10 @@ export async function gerarResumoProjetoIa(entrada, pastaSaidaOpcional, escrever
142
183
  entradaCanonica,
143
184
  agentContextPack,
144
185
  guiaPorCapacidade,
145
- topArquivosCodigoDetectados: arquivosCodigoDetectados,
186
+ topArquivosCodigoDetectados: arquivosCodigoDetectados.map((item) => {
187
+ const rel = path.relative(contextoProjeto.baseProjeto, item);
188
+ return rel && !rel.startsWith("..") ? rel.replace(/[\\/]/g, "/") : item.replace(/[\\/]/g, "/");
189
+ }),
146
190
  modulos,
147
191
  };
148
192
  const micro = [
@@ -14,7 +14,7 @@ import { resolverAnaliseDriftConsultaCli, resolverOpcoesDriftCli } from "./index
14
14
  export async function comandoInspecionar(entrada, emJson, cwd = process.cwd(), args = []) {
15
15
  const analiseDrift = resolverAnaliseDriftConsultaCli(args, "inspecionar");
16
16
  const alvo = entrada ? path.resolve(cwd, entrada) : cwd;
17
- const alvoEhArquivo = (await stat(alvo)).isFile();
17
+ const alvoEhArquivo = (await stat(alvo).catch(() => undefined))?.isFile() ?? false;
18
18
  const escopoEstreito = analiseDrift.opcoes.escopo === "arquivo"
19
19
  || analiseDrift.opcoes.escopo === "modulo"
20
20
  || (analiseDrift.opcoes.escopo === undefined
@@ -291,7 +291,7 @@ export async function comandoResumo(entrada, args, emJson) {
291
291
  const escreverNaRaiz = possuiFlag(args, "--raiz");
292
292
  const alvo = entrada ? path.resolve(process.cwd(), entrada) : process.cwd();
293
293
  const analiseDrift = resolverAnaliseDriftConsultaCli(args, "resumo");
294
- const alvoEhArquivo = (await stat(alvo)).isFile();
294
+ const alvoEhArquivo = (await stat(alvo).catch(() => undefined))?.isFile() ?? false;
295
295
  if (alvoEhArquivo) {
296
296
  const contexto = await carregarContextoModuloIa(alvo, analiseDrift);
297
297
  const resumoSemantico = coletarResumoSemanticoModulo(contexto);
@@ -36,6 +36,11 @@ export async function principal() {
36
36
  case "iniciar":
37
37
  codigoSaida = await comandoIniciar(cwd, normalizarTemplateIniciar(obterOpcao(resto, "--template")), { force: possuiFlag(resto, "--force"), exemplosCompletos: possuiFlag(resto, "--com-exemplos") });
38
38
  break;
39
+ case "sessao": {
40
+ const { comandoSessao } = await import("./comandoSessao.js");
41
+ codigoSaida = await comandoSessao(posicionais, resto, possuiFlag(resto, "--json"), cwd);
42
+ break;
43
+ }
39
44
  case "author":
40
45
  codigoSaida = await comandoAuthor(posicionais, resto, possuiFlag(resto, "--json"));
41
46
  break;
@@ -1,2 +1,8 @@
1
1
  import type { ContextoProjetoCarregado, OpcoesCarregarProjeto } from './projetoTipos.js';
2
+ export declare class ErroContratoNaoEncontrado extends Error {
3
+ readonly caminhoTentado: string;
4
+ readonly sugeridos: string[];
5
+ constructor(mensagem: string, caminhoTentado: string, sugeridos: string[]);
6
+ paraControleCli(): import("./cliControlError.js").CliControlError;
7
+ }
2
8
  export declare function carregarProjeto(entrada: string | undefined, cwd: string, opcoes?: OpcoesCarregarProjeto): Promise<ContextoProjetoCarregado>;
@@ -1,6 +1,7 @@
1
1
  // SEMA-GOVERNED: sema.produto.governanca_ia.contexto, sema.produto.governanca_ia.drift.cache.modos
2
2
  // Descrição: orquestra leitura de contratos, compilação e seleção de módulos do projeto.
3
3
  import { stat } from 'node:fs/promises';
4
+ import { erroContratoNaoEncontrado } from './cliControlError.js';
4
5
  import path from 'node:path';
5
6
  import { compilarProjeto, listarArquivosSema } from '@sema/nucleo';
6
7
  import { canonicalizarContratosNoProjeto, carregarFechamentoContratosSema, carregarFontesContratos, } from './projetoCatalogo.js';
@@ -19,12 +20,53 @@ function resolverFontesLegadoDeclaradas(configCarregada) {
19
20
  .filter((fonte) => Boolean(fonte)))]
20
21
  .sort((a, b) => a.localeCompare(b, "pt-BR"));
21
22
  }
23
+ export class ErroContratoNaoEncontrado extends Error {
24
+ caminhoTentado;
25
+ sugeridos;
26
+ constructor(mensagem, caminhoTentado, sugeridos) {
27
+ super(mensagem);
28
+ this.caminhoTentado = caminhoTentado;
29
+ this.sugeridos = sugeridos;
30
+ this.name = "ErroContratoNaoEncontrado";
31
+ }
32
+ paraControleCli() {
33
+ return erroContratoNaoEncontrado({ caminhoTentado: this.caminhoTentado, sugeridos: this.sugeridos });
34
+ }
35
+ }
36
+ async function sugerirContratosProximos(baseProjeto, nomeArquivo) {
37
+ const origens = await resolverOrigensProjeto(baseProjeto, baseProjeto, undefined);
38
+ const encontrados = [];
39
+ for (const origem of origens) {
40
+ const arquivos = await listarArquivosDeOrigens([origem]);
41
+ for (const arquivo of arquivos) {
42
+ encontrados.push(path.relative(baseProjeto, arquivo).replace(/\\/g, "/"));
43
+ }
44
+ }
45
+ const alvo = nomeArquivo.replace(/\.sema$/i, "").toLowerCase();
46
+ return encontrados
47
+ .map((relativo) => ({
48
+ relativo,
49
+ pontuacao: relativo.toLowerCase().includes(alvo) ? 0 : 1,
50
+ }))
51
+ .sort((a, b) => a.pontuacao - b.pontuacao || a.relativo.localeCompare(b.relativo, "pt-BR"))
52
+ .map((item) => item.relativo).slice(0, 5);
53
+ }
22
54
  export async function carregarProjeto(entrada, cwd, opcoes = {}) {
23
55
  const entradaBase = entrada ? path.resolve(cwd, entrada) : cwd;
24
56
  const configCarregada = await carregarConfiguracaoProjeto(entradaBase);
25
57
  const entradaResolvida = entrada ? path.resolve(cwd, entrada) : await resolverEntradaPadrao(cwd, configCarregada);
58
+ const infoEntrada = await stat(entradaResolvida).catch(() => undefined);
59
+ if (!infoEntrada) {
60
+ const baseSugestao = configCarregada?.baseDiretorio ?? path.dirname(entradaResolvida);
61
+ const contratosProximos = entradaResolvida.toLowerCase().endsWith(".sema")
62
+ ? await sugerirContratosProximos(baseSugestao, path.basename(entradaResolvida))
63
+ : [];
64
+ throw erroContratoNaoEncontrado({
65
+ caminhoTentado: entradaResolvida,
66
+ sugeridos: contratosProximos.slice(0, 5),
67
+ });
68
+ }
26
69
  const baseProjeto = await resolverBaseProjeto(entradaResolvida, configCarregada);
27
- const infoEntrada = await stat(entradaResolvida);
28
70
  if (infoEntrada.isFile() && path.extname(entradaResolvida).toLowerCase() !== ".sema") {
29
71
  throw new Error(`Entrada de contrato precisa terminar em .sema: ${entradaResolvida}`);
30
72
  }
@@ -1,7 +1,7 @@
1
1
  import { type EntradaEnvelopeResultadoCliV1, type EnvelopeResultadoCliV1 } from "./resultadoCli.js";
2
2
  export { CODIGO_SAIDA_FATAL_RUNTIME_CLI, SCHEMA_RESULTADO_CLI_V1, criarEnvelopeResultadoCliV1, interpretarPayloadRuntimeCli, type EntradaEnvelopeResultadoCliV1, type EnvelopeResultadoCliV1, type TipoResultadoCliV1, } from "./resultadoCli.js";
3
3
  export declare const SCHEMA_CONTROLE_CLI_V1: "sema.cli.control/v1";
4
- export type TipoControleCli = "HELP" | "UNKNOWN_COMMAND" | "ARGUMENT_ERROR" | "FATAL_ERROR";
4
+ export type TipoControleCli = "HELP" | "UNKNOWN_COMMAND" | "ARGUMENT_ERROR" | "CONTRACT_NOT_FOUND" | "FATAL_ERROR";
5
5
  export interface EnvelopeControleJsonV1 {
6
6
  readonly schemaVersion: typeof SCHEMA_CONTROLE_CLI_V1;
7
7
  readonly ok: boolean;
@@ -9,12 +9,14 @@ export interface EnvelopeControleJsonV1 {
9
9
  readonly code: string;
10
10
  readonly message: string;
11
11
  readonly exitCode: number;
12
+ readonly details?: Record<string, unknown>;
12
13
  }
13
14
  export interface EntradaEnvelopeControleJsonV1 {
14
15
  readonly categoria: TipoControleCli;
15
16
  readonly codigoPublico: string;
16
17
  readonly mensagemPublica: string;
17
18
  readonly codigoSaida: number;
19
+ readonly detalhes?: Record<string, unknown>;
18
20
  }
19
21
  export interface OpcoesInvocacaoPublica {
20
22
  readonly resultado: TipoControleCli;
package/dist/saidaCli.js CHANGED
@@ -7,12 +7,14 @@ const CODIGO_PADRAO = {
7
7
  HELP: "CLI_HELP",
8
8
  UNKNOWN_COMMAND: "CLI_UNKNOWN_COMMAND",
9
9
  ARGUMENT_ERROR: "CLI_ARGUMENT_ERROR",
10
+ CONTRACT_NOT_FOUND: "CLI_CONTRACT_NOT_FOUND",
10
11
  FATAL_ERROR: "CLI_FATAL_ERROR",
11
12
  };
12
13
  const MENSAGEM_PADRAO = {
13
14
  HELP: "Ajuda da CLI Sema.",
14
15
  UNKNOWN_COMMAND: "Comando Sema desconhecido.",
15
16
  ARGUMENT_ERROR: "Argumentos inválidos. Consulte a ajuda do comando.",
17
+ CONTRACT_NOT_FOUND: "Contrato Sema nao encontrado. Consulte os detalhes no envelope.",
16
18
  FATAL_ERROR: "Falha ao executar a CLI da Sema.",
17
19
  };
18
20
  const CODIGO_PUBLICO_VALIDO = /^[A-Z][A-Z0-9_]{2,63}$/u;
@@ -75,6 +77,9 @@ export function criarEnvelopeControleJsonV1(entrada) {
75
77
  code: normalizarCodigoPublico(entrada.categoria, entrada.codigoPublico),
76
78
  message: normalizarMensagemPublica(entrada.categoria, entrada.mensagemPublica),
77
79
  exitCode,
80
+ ...(entrada.detalhes && entrada.categoria === "CONTRACT_NOT_FOUND"
81
+ ? { details: entrada.detalhes }
82
+ : {}),
78
83
  };
79
84
  }
80
85
  export function emitirResultadoCliJsonV1(entrada) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@semacode/cli",
3
- "version": "3.9.3",
3
+ "version": "3.10.0",
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",