@semacode/cli 4.1.2 → 4.2.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 (39) hide show
  1. package/dist/agentBootstrap.d.ts +4 -0
  2. package/dist/agentBootstrap.js +44 -0
  3. package/dist/agentContext.d.ts +1 -1
  4. package/dist/agentContext.js +5 -5
  5. package/dist/agentContextPack.js +22 -31
  6. package/dist/agentEntryPoints.d.ts +1 -3
  7. package/dist/agentEntryPoints.js +16 -262
  8. package/dist/cliArgs.js +1 -0
  9. package/dist/cliGrammar.js +8 -4
  10. package/dist/cliHelpTexto.js +5 -4
  11. package/dist/comandoSessao.d.ts +0 -33
  12. package/dist/comandoSessao.js +71 -70
  13. package/dist/contextBudget.d.ts +15 -0
  14. package/dist/contextBudget.js +64 -0
  15. package/dist/docs.part01.js +0 -1
  16. package/dist/docs.part02.js +11 -23
  17. package/dist/docsRelations.d.ts +2 -0
  18. package/dist/docsRelations.js +83 -0
  19. package/dist/geracaoCore.d.ts +1 -1
  20. package/dist/index.part07.d.ts +1 -0
  21. package/dist/index.part07.js +54 -31
  22. package/dist/index.part08.js +3 -3
  23. package/dist/indexPack.d.ts +1 -0
  24. package/dist/indexPack.js +1 -1
  25. package/dist/initCommand.d.ts +1 -0
  26. package/dist/initCommand.js +10 -1
  27. package/dist/sessionContext.d.ts +13 -0
  28. package/dist/sessionContext.js +41 -0
  29. package/dist/verificacaoPythonLocal.d.ts +17 -0
  30. package/dist/verificacaoPythonLocal.js +46 -0
  31. package/docs/ai-integration.md +5 -13
  32. package/docs/ai-workflow.md +8 -6
  33. package/docs/commands.md +1 -1
  34. package/docs/documentation.md +33 -0
  35. package/docs/testing.md +18 -0
  36. package/exemplos/python_verificacao.sema +68 -68
  37. package/node_modules/@sema/gerador-python/dist/runtime.js +65 -65
  38. package/package.json +1 -1
  39. package/skills/sema/SKILL.md +82 -169
@@ -25,11 +25,12 @@ export function criarAjudaRaiz(versao) {
25
25
  "",
26
26
  renderizarSecaoAscii("Fluxos rápidos", [
27
27
  "[1] Projeto novo / produção inicial",
28
- "sema iniciar --template <base|nestjs|fastapi|nextjs-api|nextjs-consumer|react-vite-consumer|angular-consumer|flutter-consumer|node-firebase-worker|aspnet-api|springboot-api|go-http-api|rust-axum-api|cpp-service-bridge> [--force] [--com-exemplos]",
28
+ "sema iniciar --template <base|nestjs|fastapi|nextjs-api|nextjs-consumer|react-vite-consumer|angular-consumer|flutter-consumer|node-firebase-worker|aspnet-api|springboot-api|go-http-api|rust-axum-api|cpp-service-bridge> [--janela-contexto <tokens>] [--orcamento-contexto <tokens>] [--reserva-saida <tokens>] [--force] [--com-exemplos]",
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]",
@@ -144,7 +145,7 @@ export function criarAjudaRaiz(versao) {
144
145
  "sema contexto-ia <arquivo.sema> [--saida <diretorio>] [--capacidade <fraca|media|forte>] [--pacote] [--json]",
145
146
  "sema sync-codex [--json]",
146
147
  "sema instalar-exemplos [--json]",
147
- "sema sessao [intencao] [entrada] [--json]",
148
+ "sema sessao [intencao] [entrada] [--arquivo <contrato-ou-codigo>] [--janela-contexto <tokens>] [--orcamento-contexto <tokens>] [--reserva-saida <tokens>] [--salvar-perfil] [--json]",
148
149
  "sema docs-impacto --intencao <acao> [--arquivo <caminho>] [--criar-ausentes] [--completo] [--json]",
149
150
  "sema finalizar-mudanca --intencao <acao> [--arquivo <caminho>] [--doc-lida <caminho>] [--json]",
150
151
  "sema author briefing <arquivo.sema> [--json]",
@@ -1,34 +1 @@
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
1
  export declare function comandoSessao(posicionais: string[], args: string[], emJson: boolean, cwd?: string): Promise<number>;
@@ -1,86 +1,87 @@
1
1
  // SEMA-GOVERNED: sema.produto.cli_sessao
2
- // Descricao: comando sessao devolve envelope compacto de abertura governada.
2
+ // Descrição: abertura progressiva com perfil explícito, frescor e navegação limitada por orçamento.
3
3
  import path from "node:path";
4
4
  import { verificarFrescorArtefatos } from "./index.part04.js";
5
5
  import { carregarProjeto } from "./projetoCarregar.js";
6
+ import { carregarConfiguracaoProjeto } from "./projetoConfig.js";
6
7
  import { resolverDocumentacaoObrigatoria } from "./docs.js";
8
+ import { resolverPerfilContexto, salvarPerfilContexto, estimarRespostaTokens, PERGUNTA_CONTEXTO } from "./contextBudget.js";
9
+ import { navegarContextoSessao } from "./sessionContext.js";
10
+ function opcao(args, nome) {
11
+ const i = args.findIndex((arg) => arg === nome || arg.startsWith(`${nome}=`));
12
+ return i < 0 ? undefined : args[i].startsWith(`${nome}=`) ? args[i].slice(nome.length + 1) : args[i + 1];
13
+ }
7
14
  export async function comandoSessao(posicionais, args, emJson, cwd = process.cwd()) {
8
- const intencao = posicionais[0] ?? args.find((arg) => arg.startsWith("--intencao="))?.split("=")[1] ?? "";
15
+ const emitir = (payload, codigo = 0) => {
16
+ if (emJson)
17
+ console.log(JSON.stringify(payload));
18
+ else {
19
+ const p = payload;
20
+ console.log(`Sessão Sema — ${p.status ?? "consulta"}`);
21
+ for (const candidato of p.contexto?.candidatos ?? [])
22
+ console.log(` ${candidato.contrato} — ${candidato.motivo}`);
23
+ for (const passo of p.proximosPassos ?? [])
24
+ console.log(` → ${passo}`);
25
+ }
26
+ return codigo;
27
+ };
28
+ const intencao = posicionais[0] ?? "";
29
+ if (intencao.length > 2048)
30
+ return emitir({ comando: "sessao", sucesso: false, status: "intencao_longa", proximosPassos: ["Descreva a intenção em até 2048 caracteres e informe --arquivo para fixar o escopo."] }, 1);
9
31
  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,
32
+ const configuracao = await carregarConfiguracaoProjeto(entrada ? path.resolve(cwd, entrada) : cwd);
33
+ const base = configuracao?.baseDiretorio ?? cwd;
34
+ const perfil = await resolverPerfilContexto(base, args);
35
+ if (!perfil)
36
+ return emitir({
37
+ comando: "sessao", sucesso: true, status: "configuracao_necessaria", execucaoAutorizada: false,
38
+ proximosPassos: [PERGUNTA_CONTEXTO, 'Repita sema sessao "<intenção>" --janela-contexto <tokens> --salvar-perfil --json. Não infira a janela pelo modelo.'],
27
39
  });
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
- },
40
+ if (args.includes("--salvar-perfil"))
41
+ await salvarPerfilContexto(base, perfil);
42
+ const projeto = await carregarProjeto(entrada, cwd, { escopo: "projeto", adiarDescobertaCodigo: true });
43
+ const frescor = await verificarFrescorArtefatos(projeto.baseProjeto);
44
+ const pacoteCli = (await import("../package.json", { with: { type: "json" } })).default;
45
+ const arquivo = opcao(args, "--arquivo");
46
+ const contexto = frescor.fresco && intencao.trim() ? await navegarContextoSessao(projeto.baseProjeto, intencao, arquivo) : undefined;
47
+ const contrato = contexto?.candidatos.length === 1 ? contexto.candidatos[0].contrato : undefined;
48
+ const docs = contrato ? await resolverDocumentacaoObrigatoria({ baseProjeto: projeto.baseProjeto, intencao, arquivosAlvo: [contrato], criarAusentes: false }) : undefined;
49
+ const alvo = contrato ? JSON.stringify(contrato) : "<contrato>";
50
+ const payload = {
51
+ comando: "sessao", sucesso: true,
52
+ status: !frescor.fresco ? "indice_desatualizado" : contexto?.status ?? "intencao_necessaria",
53
+ execucaoAutorizada: false,
54
+ workspace: { baseProjeto: projeto.baseProjeto, nomePasta: path.basename(projeto.baseProjeto), hashContratos: frescor.hashAtual, cliVersao: pacoteCli.version },
43
55
  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
- },
56
+ perfil,
57
+ contexto: contexto ? { ...contexto, candidatosOmitidos: 0 } : undefined,
58
+ intencoes: { intencao, docsBloqueantes: docs?.leituraObrigatoria.map((doc) => doc.relativo) ?? [], leituraPendente: true },
54
59
  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',
60
+ drift: `sema drift ${alvo} --cache fresh --json`,
61
+ validar: `sema validar ${alvo} --json`,
62
+ verificar: `sema verificar ${alvo} --alvo <alvo> --json`,
63
+ finalizar: `sema finalizar-mudanca --intencao "<intenção>" --arquivo ${alvo} --doc-lida <doc> --json`,
59
64
  },
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
+ proximosPassos: !frescor.fresco ? ["Rode sema sync-codex --json e repita a sessão."]
66
+ : contrato ? [`Leia ${contrato}; consulte docs-impacto com esse --arquivo para motivos e dependências. Depois inspecione apenas o código vinculado.`, "Referências do índice são navegação. Confirme regras completas e gates antes de agir."]
67
+ : ["Informe uma intenção específica ou --arquivo <contrato-ou-código> para resolver o escopo. Não edite enquanto houver ambiguidade."],
68
+ orcamento: { metodo: "chars_div_4", tokensEstimadosResposta: 0, limiteTokens: perfil.orcamentoTokens, contagemExata: false, contextoDaConversaMedido: false },
65
69
  };
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}`);
70
+ while (estimarRespostaTokens(payload) > perfil.orcamentoTokens && payload.contexto?.candidatos.length) {
71
+ payload.contexto.candidatos.pop();
72
+ payload.contexto.candidatosOmitidos++;
80
73
  }
81
- for (const passo of envelope.proximosPassos) {
82
- console.log(` → ${passo}`);
74
+ if (payload.contexto?.candidatosOmitidos) {
75
+ payload.status = "refinar_escopo";
76
+ payload.proximosPassos = ["Há candidatos omitidos pelo orçamento. Refine a intenção ou informe --arquivo; não interprete omissão como ausência de dependência."];
83
77
  }
84
- return 0;
78
+ for (let i = 0; i < 3; i++)
79
+ payload.orcamento.tokensEstimadosResposta = estimarRespostaTokens(payload);
80
+ if (payload.orcamento.tokensEstimadosResposta > perfil.orcamentoTokens)
81
+ return emitir({
82
+ comando: "sessao", sucesso: false, status: "orcamento_insuficiente", execucaoAutorizada: false,
83
+ proximosPassos: ["Metadados e leituras bloqueantes excedem o orçamento. Refine --arquivo ou ajuste --orcamento-contexto; nenhuma leitura foi dispensada."],
84
+ }, 1);
85
+ return emitir(payload);
85
86
  }
86
87
  //# sourceMappingURL=comandoSessao.js.map
@@ -0,0 +1,15 @@
1
+ export declare const ARQUIVO_PERFIL_CONTEXTO = ".sema/contexto-ia.json";
2
+ export declare const OPCOES_CONTEXTO: readonly ["--janela-contexto", "--orcamento-contexto", "--reserva-saida"];
3
+ export declare const PERGUNTA_CONTEXTO = "Qual \u00E9 a janela de contexto efetivamente configurada para esta IA, em tokens?";
4
+ export interface PerfilContexto {
5
+ schema: "sema.agent-context-profile/v1";
6
+ janelaTokens: number;
7
+ orcamentoTokens: number;
8
+ reservaSaidaTokens: number;
9
+ }
10
+ export declare function criarPerfilContexto(janela: number, orcamento?: number, reserva?: number): PerfilContexto;
11
+ export declare function numeroOpcaoContexto(args: readonly string[], nome: string): number | undefined;
12
+ export declare function lerPerfilContexto(base: string): Promise<PerfilContexto | undefined>;
13
+ export declare function resolverPerfilContexto(base: string, args: readonly string[]): Promise<PerfilContexto | undefined>;
14
+ export declare function salvarPerfilContexto(base: string, perfil: PerfilContexto): Promise<void>;
15
+ export declare function estimarRespostaTokens(payload: unknown): number;
@@ -0,0 +1,64 @@
1
+ // SEMA-GOVERNED: sema.produto.cli_sessao
2
+ // Descrição: perfil local explícito e orçamento estimado de contexto, sem inferir capacidade pelo modelo.
3
+ import { readFile } from "node:fs/promises";
4
+ import path from "node:path";
5
+ import { escreverArquivoWorkspaceSeguro, validarDestinosEscritaWorkspace } from "./workspaceWrite.js";
6
+ export const ARQUIVO_PERFIL_CONTEXTO = ".sema/contexto-ia.json";
7
+ export const OPCOES_CONTEXTO = ["--janela-contexto", "--orcamento-contexto", "--reserva-saida"];
8
+ export const PERGUNTA_CONTEXTO = "Qual é a janela de contexto efetivamente configurada para esta IA, em tokens?";
9
+ export function criarPerfilContexto(janela, orcamento, reserva) {
10
+ const perfil = {
11
+ schema: "sema.agent-context-profile/v1",
12
+ janelaTokens: janela,
13
+ orcamentoTokens: orcamento ?? Math.min(4096, Math.floor(janela / 8)),
14
+ reservaSaidaTokens: reserva ?? Math.min(4096, Math.floor(janela / 4)),
15
+ };
16
+ if (![perfil.janelaTokens, perfil.orcamentoTokens, perfil.reservaSaidaTokens].every(Number.isSafeInteger)
17
+ || janela < 4096 || perfil.orcamentoTokens < 512 || perfil.reservaSaidaTokens < 256
18
+ || perfil.orcamentoTokens + perfil.reservaSaidaTokens > janela - 1024) {
19
+ throw new Error("Perfil inválido: janela >= 4096, orçamento >= 512, reserva >= 256 e pelo menos 1024 tokens livres para instruções e conversa.");
20
+ }
21
+ return perfil;
22
+ }
23
+ export function numeroOpcaoContexto(args, nome) {
24
+ const indice = args.findIndex((arg) => arg === nome || arg.startsWith(`${nome}=`));
25
+ if (indice < 0)
26
+ return undefined;
27
+ const raw = args[indice].startsWith(`${nome}=`) ? args[indice].slice(nome.length + 1) : args[indice + 1];
28
+ if (!raw || !/^[1-9]\d*$/u.test(raw) || !Number.isSafeInteger(Number(raw))) {
29
+ throw new Error(`${nome} exige um inteiro positivo em tokens.`);
30
+ }
31
+ return Number(raw);
32
+ }
33
+ export async function lerPerfilContexto(base) {
34
+ const [destino] = await validarDestinosEscritaWorkspace(base, [ARQUIVO_PERFIL_CONTEXTO]);
35
+ if (!destino?.existe)
36
+ return undefined;
37
+ const perfil = JSON.parse(await readFile(path.join(base, ARQUIVO_PERFIL_CONTEXTO), "utf8"));
38
+ if (perfil.schema !== "sema.agent-context-profile/v1")
39
+ throw new Error("Schema do perfil de contexto inválido.");
40
+ return criarPerfilContexto(perfil.janelaTokens, perfil.orcamentoTokens, perfil.reservaSaidaTokens);
41
+ }
42
+ export async function resolverPerfilContexto(base, args) {
43
+ const janela = numeroOpcaoContexto(args, "--janela-contexto");
44
+ const orcamento = numeroOpcaoContexto(args, "--orcamento-contexto");
45
+ const reserva = numeroOpcaoContexto(args, "--reserva-saida");
46
+ const salvo = await lerPerfilContexto(base);
47
+ if (janela !== undefined)
48
+ return criarPerfilContexto(janela, orcamento, reserva);
49
+ if (!salvo) {
50
+ if (orcamento !== undefined || reserva !== undefined)
51
+ throw new Error("Informe --janela-contexto antes de ajustar o orçamento.");
52
+ return undefined;
53
+ }
54
+ return criarPerfilContexto(salvo.janelaTokens, orcamento ?? salvo.orcamentoTokens, reserva ?? salvo.reservaSaidaTokens);
55
+ }
56
+ export async function salvarPerfilContexto(base, perfil) {
57
+ await escreverArquivoWorkspaceSeguro(base, ARQUIVO_PERFIL_CONTEXTO, `${JSON.stringify(perfil, null, 2)}\n`, { sobrescrever: true });
58
+ }
59
+ // Reserva conservadora para o envelope público e a indentação que o adaptador acrescenta.
60
+ export function estimarRespostaTokens(payload) {
61
+ const envelope = { schemaVersion: "sema.cli.result/v1", ok: true, kind: "SUCCESS", command: "sessao", code: "CLI_SUCCESS", message: null, exitCode: 0, payload };
62
+ return Math.ceil((JSON.stringify(envelope, null, 2).length + 256) / 4);
63
+ }
64
+ //# sourceMappingURL=contextBudget.js.map
@@ -309,7 +309,6 @@ export function resumoConteudo(conteudo) {
309
309
  export async function adicionarDocsRaizExistentes(baseProjeto, registrar) {
310
310
  const docsRaiz = [
311
311
  { relativo: "AGENTS.md", tipo: "raiz", motivo: "regras obrigatorias do agente no projeto" },
312
- { relativo: "SEMA_INDEX.json", tipo: "raiz", motivo: "indice IA-first do projeto" },
313
312
  { relativo: "README.md", tipo: "raiz", motivo: "visao geral publica do projeto" },
314
313
  { relativo: "docs/README.md", tipo: "documentacao", motivo: "indice de documentacao" },
315
314
  ];
@@ -2,29 +2,12 @@
2
2
  // Descricao: resolve e cria documentacao obrigatoria sem escapar do workspace nem gravar lotes parcialmente.
3
3
  import { readFile } from "node:fs/promises";
4
4
  import path from "node:path";
5
+ import { resolverRelacoesDocumentais } from "./docsRelations.js";
5
6
  import { emitirDiagnosticosArquivosOrcamento } from "./driftOrcamento.js";
6
7
  import { escreverArquivoWorkspaceSeguro, validarDestinosEscritaWorkspace, } from "./workspaceWrite.js";
7
- import { REGRAS_DOCUMENTACAO, adicionarDocsRaizExistentes, adicionarReadmesDeArquivos, caminhoEstaDentro, caminhoExiste, criarTemplateDoc, documentoPareceTemplateCriadoPelaSema, documentoTemSubstancia, inferirCategorias, listarArquivosRecursivo, normalizarRelativo, normalizarTexto, resumoConteudo, termosRelacionamentoContratos } from "./docs.part01.js";
8
+ import { REGRAS_DOCUMENTACAO, adicionarDocsRaizExistentes, adicionarReadmesDeArquivos, caminhoEstaDentro, caminhoExiste, criarTemplateDoc, documentoPareceTemplateCriadoPelaSema, documentoTemSubstancia, inferirCategorias, normalizarRelativo, resumoConteudo } from "./docs.part01.js";
8
9
  export async function adicionarContratosRelacionados(baseProjeto, intencao, arquivosAlvo, registrar) {
9
- const pastaContratos = path.join(baseProjeto, "contratos");
10
- if (!(await caminhoExiste(pastaContratos))) {
11
- return;
12
- }
13
- const termos = termosRelacionamentoContratos(intencao, arquivosAlvo);
14
- const arquivos = (await listarArquivosRecursivo(pastaContratos)).filter((arquivo) => arquivo.endsWith(".sema"));
15
- for (const arquivo of arquivos) {
16
- const relativo = normalizarRelativo(path.relative(baseProjeto, arquivo));
17
- const nome = normalizarTexto(relativo);
18
- const relacionado = termos.some((parte) => nome.includes(parte));
19
- if (relacionado) {
20
- registrar({
21
- relativo,
22
- tipo: "contrato",
23
- motivo: "contrato Sema relacionado a intencao declarada",
24
- permitirCriacao: false,
25
- });
26
- }
27
- }
10
+ await resolverRelacoesDocumentais(baseProjeto, intencao, arquivosAlvo, registrar);
28
11
  }
29
12
  export function criarRegistrador(baseProjeto) {
30
13
  const docs = [];
@@ -34,6 +17,11 @@ export function criarRegistrador(baseProjeto) {
34
17
  registrar(doc) {
35
18
  const relativo = normalizarRelativo(doc.relativo);
36
19
  const absoluto = path.resolve(baseProjeto, relativo);
20
+ if (vistos.has(relativo) && doc.obrigatoriedade !== "recomendada") {
21
+ const anterior = docs.find((item) => item.relativo === relativo);
22
+ if (anterior)
23
+ Object.assign(anterior, doc, { relativo, obrigatoriedade: "bloqueante" });
24
+ }
37
25
  if (!caminhoEstaDentro(baseProjeto, absoluto) || vistos.has(relativo)) {
38
26
  return;
39
27
  }
@@ -86,7 +74,7 @@ export async function resolverDocumentacaoObrigatoria(opcoes) {
86
74
  existe,
87
75
  criado,
88
76
  criacaoAutomatica: doc.permitirCriacao,
89
- obrigatorio: true,
77
+ obrigatorio: doc.obrigatoriedade !== "recomendada",
90
78
  obrigatoriedade: doc.obrigatoriedade ?? "bloqueante",
91
79
  template: existe ? undefined : template,
92
80
  };
@@ -100,7 +88,7 @@ export async function resolverDocumentacaoObrigatoria(opcoes) {
100
88
  }
101
89
  leituraObrigatoria.push(item);
102
90
  }
103
- const docsAusentes = leituraObrigatoria.filter((doc) => !doc.existe);
91
+ const docsAusentes = leituraObrigatoria.filter((doc) => !doc.existe && doc.obrigatoriedade !== "recomendada");
104
92
  const docsCriadas = leituraObrigatoria.filter((doc) => doc.criado);
105
93
  const bloqueios = docsAusentes.filter((doc) => doc.obrigatoriedade !== "recomendada").map((doc) => ({
106
94
  tipo: "documentacao_ausente",
@@ -116,7 +104,7 @@ export async function resolverDocumentacaoObrigatoria(opcoes) {
116
104
  intencao,
117
105
  categorias,
118
106
  arquivosAlvo,
119
- leituraObrigatoria,
107
+ leituraObrigatoria: leituraObrigatoria.filter((doc) => doc.obrigatoriedade !== "recomendada"),
120
108
  docsAusentes,
121
109
  docsCriadas,
122
110
  leituraRecomendada: leituraObrigatoria
@@ -0,0 +1,2 @@
1
+ import { type DocumentoPlanejado } from "./docs.part01.js";
2
+ export declare function resolverRelacoesDocumentais(base: string, intencao: string, alvos: string[], registrar: (doc: DocumentoPlanejado) => void): Promise<void>;
@@ -0,0 +1,83 @@
1
+ // SEMA-GOVERNED: sema.produto.governanca_ia.documentacao
2
+ // Descrição: resolve contrato exato, vínculos e dependências declaradas; palavras são apenas recomendações.
3
+ import { readFile } from "node:fs/promises";
4
+ import path from "node:path";
5
+ import { tokenizar, parsear, listarCandidatosUseRelativo } from "@sema/nucleo";
6
+ import { validarDestinosEscritaWorkspace } from "./workspaceWrite.js";
7
+ import { caminhoExiste, caminhoEstaDentro, listarArquivosRecursivo, normalizarRelativo, normalizarTexto, termosRelacionamentoContratos } from "./docs.part01.js";
8
+ export async function resolverRelacoesDocumentais(base, intencao, alvos, registrar) {
9
+ const relativo = (p) => {
10
+ const absoluto = path.resolve(base, p);
11
+ if (!caminhoEstaDentro(base, absoluto))
12
+ throw new Error("Referência documental fora do workspace.");
13
+ return normalizarRelativo(path.relative(base, absoluto));
14
+ };
15
+ const explicitos = alvos.map(relativo);
16
+ const pasta = path.join(base, "contratos");
17
+ const caminhos = await caminhoExiste(pasta) ? (await listarArquivosRecursivo(pasta, Number.POSITIVE_INFINITY)).filter((p) => p.endsWith(".sema")).map(relativo) : [];
18
+ const todos = [...new Set([...caminhos, ...explicitos.filter((p) => p.endsWith(".sema"))])];
19
+ await validarDestinosEscritaWorkspace(base, todos);
20
+ const catalogo = new Map();
21
+ for (const p of todos) {
22
+ if (!await caminhoExiste(path.join(base, p)))
23
+ continue;
24
+ const lexer = tokenizar(await readFile(path.join(base, p), "utf8"), p);
25
+ const parser = parsear(lexer.tokens);
26
+ if (parser.modulo && lexer.diagnosticos.length === 0 && parser.diagnosticos.length === 0)
27
+ catalogo.set(p, parser.modulo);
28
+ }
29
+ const fila = explicitos.filter((p) => p.endsWith(".sema"))
30
+ .map((arquivo) => ({ arquivo, motivo: "Contrato informado explicitamente em --arquivo" }));
31
+ for (const [arquivo, modulo] of catalogo) {
32
+ const donos = [modulo, ...modulo.tasks, ...modulo.routes, ...modulo.flows];
33
+ for (const dono of donos) {
34
+ for (const campo of dono.vinculos?.campos ?? []) {
35
+ if (campo.nome !== "arquivo")
36
+ continue;
37
+ const vinculo = relativo(campo.valor.replace(/^["']|["']$/g, ""));
38
+ if (explicitos.includes(vinculo))
39
+ fila.push({ arquivo, motivo: `Vínculo declarado em ${modulo.nome}: ${vinculo}` });
40
+ }
41
+ }
42
+ }
43
+ const vistos = new Set();
44
+ for (let i = 0; i < fila.length; i++) {
45
+ const item = fila[i];
46
+ if (vistos.has(item.arquivo))
47
+ continue;
48
+ vistos.add(item.arquivo);
49
+ registrar({ relativo: item.arquivo, tipo: "contrato", motivo: item.motivo, permitirCriacao: false });
50
+ const modulo = catalogo.get(item.arquivo);
51
+ if (!modulo) {
52
+ if (await caminhoExiste(path.join(base, item.arquivo)))
53
+ throw new Error(`Contrato inválido: ${item.arquivo}. Rode sema validar.`);
54
+ continue; // O resolvedor público transforma o contrato ausente em bloqueio.
55
+ }
56
+ for (const use of modulo.uses.filter((u) => u.origem === "sema")) {
57
+ let encontrados = [];
58
+ for (const nome of [use.caminho, ...listarCandidatosUseRelativo(modulo.nome, use.caminho)]) {
59
+ encontrados = [...catalogo].filter(([, m]) => m.nome === nome);
60
+ if (encontrados.length)
61
+ break;
62
+ }
63
+ if (encontrados.length !== 1)
64
+ throw new Error(`Dependência use ${use.caminho} de ${item.arquivo} ausente ou ambígua. Rode sema validar.`);
65
+ fila.push({ arquivo: encontrados[0][0], motivo: `Dependência use ${use.caminho} declarada em ${item.arquivo}` });
66
+ }
67
+ for (const campo of modulo.docs?.campos ?? []) {
68
+ if (campo.nome !== "arquivo")
69
+ continue;
70
+ const doc = relativo(campo.valor.replace(/^["']|["']$/g, ""));
71
+ const motivo = `Referência docs.arquivo declarada em ${item.arquivo}`;
72
+ registrar({ relativo: doc, tipo: doc.endsWith(".sema") ? "contrato" : "documentacao", motivo, permitirCriacao: false, obrigatoriedade: "recomendada" });
73
+ }
74
+ }
75
+ // Arquivo explícito fixa o escopo; não acrescentar contratos por palavras genéricas.
76
+ if (explicitos.length > 0)
77
+ return;
78
+ const termos = termosRelacionamentoContratos(intencao, []);
79
+ const sugestoes = [...catalogo].filter(([arquivo]) => termos.some((t) => normalizarTexto(path.basename(arquivo, ".sema")).includes(t))).slice(0, 8);
80
+ for (const [arquivo] of sugestoes)
81
+ registrar({ relativo: arquivo, tipo: "contrato", motivo: "Sugestão por intenção; confirme o contrato usando --arquivo", permitirCriacao: false, obrigatoriedade: "recomendada" });
82
+ }
83
+ //# sourceMappingURL=docsRelations.js.map
@@ -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;