@semacode/cli 3.12.1 → 3.13.1
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/agentEntryPoints.js +9 -9
- package/dist/cliHelpTexto.js +1 -1
- package/dist/drift.contrato.d.ts +1 -0
- package/dist/drift.contrato.js +1 -0
- package/dist/drift.part01.d.ts +4 -2
- package/dist/drift.part11.js +12 -1
- package/dist/drift.part14.d.ts +3 -0
- package/dist/drift.part14.js +108 -10
- package/dist/driftEscopo.d.ts +1 -0
- package/dist/driftEscopo.js +12 -1
- package/dist/index.part03.d.ts +2 -0
- package/dist/index.part03.js +6 -0
- package/dist/index.part05.js +7 -0
- package/dist/initCommand.js +38 -2
- package/docs/ai-workflow.md +2 -2
- package/docs/commands.md +4 -4
- package/package.json +1 -1
package/dist/agentEntryPoints.js
CHANGED
|
@@ -201,7 +201,7 @@ Score composto, \`achados[]\` e \`decisaoAgente\` são sinais para guiar a próx
|
|
|
201
201
|
- Consultas honestas: \`resumo\` e \`inspecionar\` usam \`--drift none\` por padrão; score, confiança, implementação, rotas e superfícies não observadas ficam nulos/não avaliados.
|
|
202
202
|
- Se uma consulta executar drift explicitamente, \`payload.analiseDrift.sucesso\` expõe o resultado e uma falha solicitada retorna exit code diferente de zero.
|
|
203
203
|
- Cache: \`sema drift\` usa \`fresh\` por padrão. \`--cache none\` ainda executa sem persistência; \`cache\` acelera extrações validadas, mas não é prova final.
|
|
204
|
-
- Fechamento governado: se o \`payload\` de \`sema drift --cache fresh --json\` retornar \`sucesso:false\`, \`vinculos_quebrados\`, \`rotas_divergentes\` ou \`impls_quebrados\`, não diga que passou limpo. Corrija e rode drift de novo.
|
|
204
|
+
- Fechamento governado: se o \`payload\` de \`sema drift --cache fresh --json\` retornar \`sucesso:false\`, \`vinculos_quebrados\`, \`rotas_divergentes\` ou \`impls_quebrados\`, não diga que passou limpo. Corrija e rode drift de novo. \`vinculos_fora_do_escopo\` é informativo: o arquivo existe no workspace fora dos \`diretoriosCodigo\`; siga o \`diretorioSugerido\` do registro para incluir o diretório no \`sema.config.json\` — não bloqueia fechamento.
|
|
205
205
|
- Experiência governada: se a tarefa cria ou altera site, sistema, app, UI, painel, jogo, CLI/TUI ou terminal, prove acabamento moderno, contextual e não genérico. Em UI estreita (ex. 390px), \`document.documentElement.scrollWidth <= document.documentElement.clientWidth\` precisa ser verdadeiro.
|
|
206
206
|
- Caminho fora do workspace local aberto pelo usuário não substitui a pasta local.
|
|
207
207
|
|
|
@@ -265,7 +265,7 @@ Leia isto primeiro se você tem pouco contexto, pouca memória, pouco tool use o
|
|
|
265
265
|
15. Payload inline acima de ${LIMITE_CARACTERES_PAYLOAD_INLINE} caracteres não é lentidão: divida por responsabilidade; não aumente timeout para tentar passar limite de transporte.
|
|
266
266
|
16. Se aparecer caminho que não pertence ao workspace local aberto pelo usuário, pare e confirme a fonte antes de agir.
|
|
267
267
|
17. Se Sema estourar por timeout local: aumente o timeout e tente de novo. Não trate isso como falha do Sema.
|
|
268
|
-
18. Para fechamento, rode \`sema drift --cache fresh --json\`; cache persistente só acelera navegação e não é prova final. Se o \`payload\` retornar \`sucesso:false\`, \`vinculos_quebrados\`, \`rotas_divergentes\` ou \`impls_quebrados\`, não conclua. Na 3.0.0, não procure campos de domínio no topo do envelope \`sema.cli.result/v1\` nem use \`ok\` como substituto do veredito do payload.
|
|
268
|
+
18. Para fechamento, rode \`sema drift --cache fresh --json\`; cache persistente só acelera navegação e não é prova final. Se o \`payload\` retornar \`sucesso:false\`, \`vinculos_quebrados\`, \`rotas_divergentes\` ou \`impls_quebrados\`, não conclua. \`vinculos_fora_do_escopo\` é informativo com \`diretorioSugerido\` para o \`sema.config.json\`; não bloqueia fechamento. Na 3.0.0, não procure campos de domínio no topo do envelope \`sema.cli.result/v1\` nem use \`ok\` como substituto do veredito do payload.
|
|
269
269
|
19. Se a tarefa tiver site, sistema, app, UI, painel, jogo, CLI/TUI ou terminal: aplique acabamento moderno, contextual, responsivo/ergonômico, com hierarquia clara, estados e evidência. Em UI, valide mobile/desktop e prove \`scrollWidth <= clientWidth\` em viewport estreito como 390px.
|
|
270
270
|
20. Não substitua o contexto Sema por AGENTS.md, README.md, busca local, inferência por nome ou bom senso.
|
|
271
271
|
|
|
@@ -486,7 +486,7 @@ Sinal e evidência:
|
|
|
486
486
|
- Score composto, \`achados[]\` e \`decisaoAgente\` orientam a ação; abaixo de 80 bloqueia, alvo evolui 0.5 ponto até 100, e nada substitui evidência concreta.
|
|
487
487
|
- Palavra-chave ou regex passando não prova governança se contrato, código e comportamento não batem.
|
|
488
488
|
- \`resumo\` e \`inspecionar\` usam \`--drift none\` por padrão; campos derivados nulos significam não avaliados, nunca zero.
|
|
489
|
-
- \`sema drift --cache fresh --json\` com \`payload.sucesso:false\`, \`payload.vinculos_quebrados\`, \`payload.rotas_divergentes\` ou \`payload.impls_quebrados\` bloqueia fechamento. Cache persistente é aceleração, não prova final.
|
|
489
|
+
- \`sema drift --cache fresh --json\` com \`payload.sucesso:false\`, \`payload.vinculos_quebrados\`, \`payload.rotas_divergentes\` ou \`payload.impls_quebrados\` bloqueia fechamento. \`payload.vinculos_fora_do_escopo\` é informativo com \`diretorioSugerido\` para os \`diretoriosCodigo\`; não bloqueia. Cache persistente é aceleração, não prova final.
|
|
490
490
|
- Caminho fora do workspace local aberto pelo usuário não substitui a pasta local.
|
|
491
491
|
|
|
492
492
|
Acabamento visual e terminal:
|
|
@@ -685,9 +685,9 @@ This is the minimum workflow for Codex in a local workspace.
|
|
|
685
685
|
|
|
686
686
|
Contract edit rule: \`.sema\` has its own size budget. Above ${LIMITE_AVISO_LINHAS_CONTRATO_SEMA} lines, plan a split by domain/capability; above ${LIMITE_BLOQUEIO_LINHAS_CONTRATO_SEMA}, do not create or edit before splitting. Do not use parte_1/parte_2 and do not force a 1:1 contract-to-file relationship; several contracts can govern the same file through \`vinculos\`.
|
|
687
687
|
|
|
688
|
-
Closing rule: the \`payload\` of \`sema drift --cache fresh --json\` must return \`sucesso:true\`. If it reports \`sucesso:false\`, \`vinculos_quebrados\`, \`rotas_divergentes\`, or \`impls_quebrados\`, the task is still blocked. Passing unit tests or a cache hit do not replace fresh green drift. In 3.0.0, \`ok\` describes the CLI result path and never replaces a command-specific domain verdict.
|
|
688
|
+
Closing rule: the \`payload\` of \`sema drift --cache fresh --json\` must return \`sucesso:true\`. If it reports \`sucesso:false\`, \`vinculos_quebrados\`, \`rotas_divergentes\`, or \`impls_quebrados\`, the task is still blocked. Passing unit tests or a cache hit do not replace fresh green drift. \`vinculos_fora_do_escopo\` is informational: the file exists in the workspace outside the configured \`diretoriosCodigo\`, and each entry carries a \`diretorioSugerido\` to add to \`sema.config.json\`; it does not block closure. In 3.0.0, \`ok\` describes the CLI result path and never replaces a command-specific domain verdict.
|
|
689
689
|
|
|
690
|
-
Focused drift rule: file and module scopes plan their physical file set before cataloging. They never fall back to a whole-project walk when no safe anchor exists. All indexers and semantic-budget checks reuse the same in-memory source read; a global walk is reserved for explicit \`--escopo projeto\`. Code discovery is deferred until that plan exists, homonymous candidates fail as ambiguous, and configured contract origins or code roots resolving outside the workspace are rejected before enumeration. \`inspecionar\`, \`impacto\`, and \`renomear-semantico\` preserve the same directed boundary. Missing local dependencies make coverage partial and block success.
|
|
690
|
+
Focused drift rule: file and module scopes plan their physical file set before cataloging. They never fall back to a whole-project walk when no safe anchor exists. All indexers and semantic-budget checks reuse the same in-memory source read; a global walk is reserved for explicit \`--escopo projeto\`. Code discovery is deferred until that plan exists, homonymous candidates fail as ambiguous, and configured contract origins or code roots resolving outside the workspace are rejected before enumeration. \`inspecionar\`, \`impacto\`, and \`renomear-semantico\` preserve the same directed boundary. Missing unambiguous local dependencies make coverage partial and block success. In Python, an unresolved \`name\` from \`from module import name\` is not itself a missing file after \`module\` resolves: it may be an exported symbol, while a real child module is still included whenever its file exists.
|
|
691
691
|
|
|
692
692
|
Cache rule: a closure gate should use \`sema drift <contrato> --escopo modulo --cache fresh --json\`. \`--cache none\` executes the same analysis without persistent-cache I/O, while \`--cache cache\` may reuse only extraction data whose schema, workspace identity, Git HEAD, plan, configuration, contracts, member paths, and strong content digests all match. Cache objects are stored in the operating system's user-cache directory, never below the workspace. A hit is acceleration, not evidence: final links, diagnostics, score, and success are always recalculated. Corruption or an unavailable cache degrades to an in-memory calculation.
|
|
693
693
|
|
|
@@ -752,7 +752,7 @@ Then read every required doc returned by \`docs-impacto\`.
|
|
|
752
752
|
|
|
753
753
|
## Contract and Discovery
|
|
754
754
|
|
|
755
|
-
- \`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]\`: creates a new Sema project and preserves existing files by default; \`--force\` is the only explicit overwrite path.
|
|
755
|
+
- \`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]\`: creates a new Sema project and preserves existing files by default; \`--force\` is the only explicit overwrite path. Inside a subfolder of an already governed workspace, the subfolder is registered under \`diretoriosCodigo\` of the ancestral \`sema.config.json\` (safe write, deduplicated) instead of creating a local config; if the parent config cannot be written, the scaffold proceeds and prints the exact line to add.
|
|
756
756
|
- \`sema validar <arquivo-ou-pasta> --json\`: validates \`.sema\` contracts.
|
|
757
757
|
- \`sema diagnosticos <arquivo.sema> --json\`: details errors and warnings.
|
|
758
758
|
- \`sema formatar <arquivo-ou-pasta>\`: formats contracts.
|
|
@@ -772,9 +772,9 @@ Then read every required doc returned by \`docs-impacto\`.
|
|
|
772
772
|
- \`sema verificar <arquivo-ou-pasta> --json\`: runs aggregated final verification.
|
|
773
773
|
- \`sema finalizar-mudanca --intencao "<acao>" --doc-lida <arquivo> --json\`: proves documentation reading before closure.
|
|
774
774
|
|
|
775
|
-
Honest closure: unwrap drift JSON and treat its command payload as the source of truth. \`payload.sucesso:false\`, non-empty \`payload.vinculos_quebrados\`, non-empty \`payload.rotas_divergentes\`, or non-empty \`payload.impls_quebrados\` mean the change is not complete yet. Do not report "clean drift" without a green payload.
|
|
775
|
+
Honest closure: unwrap drift JSON and treat its command payload as the source of truth. \`payload.sucesso:false\`, non-empty \`payload.vinculos_quebrados\`, non-empty \`payload.rotas_divergentes\`, or non-empty \`payload.impls_quebrados\` mean the change is not complete yet. Do not report "clean drift" without a green payload. \`payload.vinculos_fora_do_escopo\` is informational: the file exists in the workspace outside \`diretoriosCodigo\`, and each entry carries a \`diretorioSugerido\` for \`sema.config.json\`.
|
|
776
776
|
|
|
777
|
-
Focused drift exposes its planned, declared, inferred, and missing files plus catalog visit/read metrics in \`escopo_aplicado\`. A missing declared file or a missing code-referenced dependency is a blocker reported as \`vinculo_quebrado\`; a missing inferred candidate derived from an \`impl\` symbol is informational under \`arquivosAusentesInferidos\` and never blocks an implementation that resolved. File and module scopes fail closed without a safe anchor, with homonymous implementation candidates, or with missing local dependencies; only \`--escopo projeto\` may walk every configured code root. Logical roots such as \`src\` are probed deterministically without a discovery walk. Configured contract origins and code roots are confined before enumeration, and \`inspecionar\`, \`impacto\`, and \`renomear-semantico\` reuse the same directed boundary without reopening arbitrary external paths.
|
|
777
|
+
Focused drift exposes its planned, declared, inferred, and missing files plus catalog visit/read metrics in \`escopo_aplicado\`. A missing declared file or a missing unambiguous code-referenced dependency is a blocker reported as \`vinculo_quebrado\`; a missing inferred candidate derived from an \`impl\` symbol is informational under \`arquivosAusentesInferidos\` and never blocks an implementation that resolved. For Python \`from module import name\`, Sema still includes \`name\` when it resolves as a real submodule, but never reports the absent submodule candidate as a broken link after the parent module resolved because \`name\` may be a function, class, or variable. File and module scopes fail closed without a safe anchor, with homonymous implementation candidates, or with missing local dependencies; only \`--escopo projeto\` may walk every configured code root. Logical roots such as \`src\` are probed deterministically without a discovery walk. Configured contract origins and code roots are confined before enumeration, and \`inspecionar\`, \`impacto\`, and \`renomear-semantico\` reuse the same directed boundary without reopening arbitrary external paths.
|
|
778
778
|
|
|
779
779
|
Drift analysis and cache modes are explicit:
|
|
780
780
|
|
|
@@ -877,7 +877,7 @@ Spatial model and render mode are orthogonal: \`THREE_D + HEADLESS\` is valid, w
|
|
|
877
877
|
- Do not stop after \`sema compilar\` if the contract target files still do not exist.
|
|
878
878
|
- Do not replace \`sema compilar\` with \`sema testar\` when the contract requires generated code.
|
|
879
879
|
- Do not create a Markdown report to pretend a gate ran.
|
|
880
|
-
- Do not say drift passed when the unwrapped \`sema drift --cache fresh --json\` payload returned \`sucesso:false\`, a non-empty \`vinculos_quebrados\`, \`rotas_divergentes\`, or \`impls_quebrados\` list.
|
|
880
|
+
- Do not say drift passed when the unwrapped \`sema drift --cache fresh --json\` payload returned \`sucesso:false\`, a non-empty \`vinculos_quebrados\`, \`rotas_divergentes\`, or \`impls_quebrados\` list. \`vinculos_fora_do_escopo\` entries are informational scope hints, not broken links.
|
|
881
881
|
- Do not declare a UI responsive without mobile/desktop proof; horizontal scroll at 390px blocks closure.
|
|
882
882
|
|
|
883
883
|
Governed code policy: keep the \`SEMA-GOVERNED\` marker, split large code by real responsibility, preserve contract links, and never treat a generated output directory as the final delivery.
|
package/dist/cliHelpTexto.js
CHANGED
|
@@ -26,7 +26,7 @@ export function criarAjudaRaiz(versao) {
|
|
|
26
26
|
renderizarSecaoAscii("Fluxos rápidos", [
|
|
27
27
|
"[1] Projeto novo / produção inicial",
|
|
28
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]",
|
|
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.",
|
|
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
32
|
"sema verificar <arquivo-ou-pasta> [--saida <dir>] [--alvo <alvo>] [--sem-cache] --json",
|
package/dist/drift.contrato.d.ts
CHANGED
|
@@ -19,6 +19,7 @@ export declare function executarDriftCheck(contratos: string[], codigoVivo: stri
|
|
|
19
19
|
impls_quebrados: number;
|
|
20
20
|
vinculos_validos: number;
|
|
21
21
|
vinculos_quebrados: number;
|
|
22
|
+
vinculos_fora_do_escopo: number;
|
|
22
23
|
score: number;
|
|
23
24
|
piso_operacional: number;
|
|
24
25
|
alvo_atual: number;
|
package/dist/drift.contrato.js
CHANGED
|
@@ -30,6 +30,7 @@ export function executarDriftCheck(contratos, codigoVivo) {
|
|
|
30
30
|
impls_quebrados: implsQuebrados,
|
|
31
31
|
vinculos_validos: vinculosValidos,
|
|
32
32
|
vinculos_quebrados: vinculosQuebrados,
|
|
33
|
+
vinculos_fora_do_escopo: 0,
|
|
33
34
|
score,
|
|
34
35
|
piso_operacional: avaliacao.pontuacaoMinimaOperacional,
|
|
35
36
|
alvo_atual: avaliacao.pontuacaoAlvoAtual,
|
package/dist/drift.part01.d.ts
CHANGED
|
@@ -39,7 +39,7 @@ export interface OpcoesDriftLegado {
|
|
|
39
39
|
observador?: ObservadorOperacaoDrift | undefined;
|
|
40
40
|
}
|
|
41
41
|
export interface DiagnosticoDrift {
|
|
42
|
-
tipo: "impl_quebrado" | "task_sem_impl" | "rota_divergente" | "recurso_divergente" | "vinculo_quebrado" | "seguranca_frouxa" | "pontuacao_semantica_insuficiente" | "contrato_monolitico" | "codigo_monolitico" | "codigo_governado_sem_cabecalho" | "escopo_estreito_sem_vinculos" | "escopo_estreito_ambiguo" | "design_nao_declarado";
|
|
42
|
+
tipo: "impl_quebrado" | "task_sem_impl" | "rota_divergente" | "recurso_divergente" | "vinculo_quebrado" | "vinculo_fora_do_escopo" | "seguranca_frouxa" | "pontuacao_semantica_insuficiente" | "contrato_monolitico" | "codigo_monolitico" | "codigo_governado_sem_cabecalho" | "escopo_estreito_sem_vinculos" | "escopo_estreito_ambiguo" | "design_nao_declarado";
|
|
43
43
|
modulo: string;
|
|
44
44
|
task?: string;
|
|
45
45
|
route?: string;
|
|
@@ -149,8 +149,9 @@ export interface RegistroVinculoDrift {
|
|
|
149
149
|
valor: string;
|
|
150
150
|
arquivo?: string;
|
|
151
151
|
simbolo?: string;
|
|
152
|
-
status: "resolvido" | "parcial" | "nao_encontrado";
|
|
152
|
+
status: "resolvido" | "parcial" | "nao_encontrado" | "fora_do_escopo";
|
|
153
153
|
confianca: NivelConfiancaSemantica;
|
|
154
|
+
diretorioSugerido?: string;
|
|
154
155
|
}
|
|
155
156
|
export interface RegistroPersistenciaRealDrift {
|
|
156
157
|
modulo: string;
|
|
@@ -256,6 +257,7 @@ export interface ResultadoDrift {
|
|
|
256
257
|
impls_quebrados: RegistroImplDrift[];
|
|
257
258
|
vinculos_validos: RegistroVinculoDrift[];
|
|
258
259
|
vinculos_quebrados: RegistroVinculoDrift[];
|
|
260
|
+
vinculos_fora_do_escopo: RegistroVinculoDrift[];
|
|
259
261
|
rotas_divergentes: RegistroRotaDivergente[];
|
|
260
262
|
recursos_validos: RegistroRecursoDrift[];
|
|
261
263
|
recursos_divergentes: RegistroRecursoDrift[];
|
package/dist/drift.part11.js
CHANGED
|
@@ -10,7 +10,8 @@ import { calcularConfiancaTask, calcularRiscoOperacional, calcularScoreTask, car
|
|
|
10
10
|
import { analisarPersistenciaReal, ordenarCandidatos } from "./drift.part09.js";
|
|
11
11
|
import { simboloEhBridgeConsumer } from "./drift.part05.js";
|
|
12
12
|
import { prepararIndicesDrift } from "./drift.part13.js";
|
|
13
|
-
import {
|
|
13
|
+
import { sondarCaminhosDeclaradosExistentes } from "./driftEscopo.js";
|
|
14
|
+
import { analisarModulosSelecionadosDrift, coletarCaminhosDeclaradosVinculosDrift } from "./drift.part14.js";
|
|
14
15
|
import { resolverPoliticaPontuacaoSemantica } from "./driftScore.js";
|
|
15
16
|
function arquivosVinculadosFisicamenteDrift(contexto, modulo, task) {
|
|
16
17
|
const arquivos = new Set();
|
|
@@ -158,6 +159,7 @@ export async function analisarDriftLegado(contexto, opcoes) {
|
|
|
158
159
|
const implsQuebrados = [];
|
|
159
160
|
const vinculosValidos = [];
|
|
160
161
|
const vinculosQuebrados = [];
|
|
162
|
+
const vinculosForaDoEscopo = [];
|
|
161
163
|
const rotasDivergentes = [];
|
|
162
164
|
const recursosValidos = [];
|
|
163
165
|
const recursosDivergentes = [];
|
|
@@ -209,6 +211,12 @@ export async function analisarDriftLegado(contexto, opcoes) {
|
|
|
209
211
|
for (const substituicao of substituicoesCaminho) {
|
|
210
212
|
substituicao.implementacao.caminho = substituicao.temporario;
|
|
211
213
|
}
|
|
214
|
+
// Sonda previa: distingue vinculo apontando para arquivo inexistente de
|
|
215
|
+
// arquivo existente fora dos diretoriosCodigo (informativo, nao bloqueante).
|
|
216
|
+
const caminhosDeclaradosVinculos = coletarCaminhosDeclaradosVinculosDrift(contexto);
|
|
217
|
+
const arquivosDeclaradosExistentes = caminhosDeclaradosVinculos.length > 0
|
|
218
|
+
? await sondarCaminhosDeclaradosExistentes(caminhosDeclaradosVinculos, contexto.baseProjeto)
|
|
219
|
+
: new Map();
|
|
212
220
|
try {
|
|
213
221
|
analisarModulosSelecionadosDrift({
|
|
214
222
|
contexto,
|
|
@@ -222,6 +230,8 @@ export async function analisarDriftLegado(contexto, opcoes) {
|
|
|
222
230
|
implsQuebrados,
|
|
223
231
|
vinculosValidos,
|
|
224
232
|
vinculosQuebrados,
|
|
233
|
+
vinculosForaDoEscopo,
|
|
234
|
+
arquivosDeclaradosExistentes,
|
|
225
235
|
rotasDivergentes,
|
|
226
236
|
recursosValidos,
|
|
227
237
|
recursosDivergentes,
|
|
@@ -563,6 +573,7 @@ export async function analisarDriftLegado(contexto, opcoes) {
|
|
|
563
573
|
impls_quebrados: implsQuebrados,
|
|
564
574
|
vinculos_validos: vinculosValidos,
|
|
565
575
|
vinculos_quebrados: vinculosQuebrados,
|
|
576
|
+
vinculos_fora_do_escopo: vinculosForaDoEscopo,
|
|
566
577
|
rotas_divergentes: rotasDivergentes,
|
|
567
578
|
recursos_validos: recursosValidos,
|
|
568
579
|
recursos_divergentes: recursosDivergentes,
|
package/dist/drift.part14.d.ts
CHANGED
|
@@ -14,6 +14,7 @@ export interface GuardrailsTaskDrift {
|
|
|
14
14
|
efeitoPrivilegiado: boolean;
|
|
15
15
|
exigeSegredos: boolean;
|
|
16
16
|
}
|
|
17
|
+
export declare function coletarCaminhosDeclaradosVinculosDrift(contexto: ContextoProjetoCarregado): string[];
|
|
17
18
|
export interface ResumoVinculosTaskDrift {
|
|
18
19
|
validos: number;
|
|
19
20
|
quebrados: number;
|
|
@@ -31,6 +32,8 @@ export interface EstadoAnaliseModulosDrift {
|
|
|
31
32
|
implsQuebrados: RegistroImplDrift[];
|
|
32
33
|
vinculosValidos: RegistroVinculoDrift[];
|
|
33
34
|
vinculosQuebrados: RegistroVinculoDrift[];
|
|
35
|
+
vinculosForaDoEscopo: RegistroVinculoDrift[];
|
|
36
|
+
arquivosDeclaradosExistentes: ReadonlyMap<string, boolean>;
|
|
34
37
|
rotasDivergentes: RegistroRotaDivergente[];
|
|
35
38
|
recursosValidos: RegistroRecursoDrift[];
|
|
36
39
|
recursosDivergentes: RegistroRecursoDrift[];
|
package/dist/drift.part14.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Descricao: analisa modulos selecionados no drift; consulte contratos/sema/governanca_ia_contexto.sema antes de editar.
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { escolherArquivoPorVinculo, escolherSimboloPorVinculo } from "./drift.part03.js";
|
|
5
|
-
import { calcularRiscoOperacional, encontrarAncoraSuperficie } from "./drift.part04.js";
|
|
5
|
+
import { calcularRiscoOperacional, chaveCaminhoCanonicoDrift, encontrarAncoraSuperficie } from "./drift.part04.js";
|
|
6
6
|
import { resolverPersistenciaLocalPorTask } from "./drift.part08.js";
|
|
7
7
|
import { escolherRotasEsperadas, normalizarCaminhoRota, ordenarCandidatos, sugerirCandidatosParaImpl, sugerirCandidatosParaTaskSemImpl } from "./drift.part09.js";
|
|
8
8
|
import { coletarVinculosIr, extrairRecursosEsperados, resolverRecursoEsperado } from "./drift.part10.js";
|
|
@@ -30,8 +30,61 @@ function redigirCaminhoDeclaradoExterno(baseProjeto, valor) {
|
|
|
30
30
|
const nome = path.basename(path.normalize(valor)) || "arquivo";
|
|
31
31
|
return `[fora_do_workspace]/${nome}`;
|
|
32
32
|
}
|
|
33
|
+
function caminhoDeclaradoTentadoVinculo(vinculo) {
|
|
34
|
+
const arquivoDeclarado = vinculo.arquivo ?? (vinculo.tipo === "arquivo" ? vinculo.valor : undefined);
|
|
35
|
+
if (arquivoDeclarado) {
|
|
36
|
+
return arquivoDeclarado;
|
|
37
|
+
}
|
|
38
|
+
const recursoDeclarado = vinculo.recurso ?? (["recurso", "tabela", "fila", "cache", "storage"].includes(vinculo.tipo) ? vinculo.valor : undefined);
|
|
39
|
+
if (recursoDeclarado) {
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
const superficieDeclarada = vinculo.superficie ?? (["superficie", "rota", "worker", "cron", "webhook", "evento", "policy", "fila", "cache", "storage"].includes(vinculo.tipo) ? vinculo.valor : undefined);
|
|
43
|
+
return superficieDeclarada ?? vinculo.valor;
|
|
44
|
+
}
|
|
45
|
+
function sugerirDiretorioCodigoDrift(baseProjeto, caminhoAbsoluto) {
|
|
46
|
+
const diretorioRelativo = path.relative(path.resolve(baseProjeto), path.dirname(path.resolve(caminhoAbsoluto))).replace(/\\/g, "/");
|
|
47
|
+
return `./${diretorioRelativo || "."}`;
|
|
48
|
+
}
|
|
49
|
+
function arquivoDentroDoEscopoCodigo(contexto, caminhoAbsoluto) {
|
|
50
|
+
return contexto.diretoriosCodigo.some((diretorio) => {
|
|
51
|
+
const relativo = path.relative(path.resolve(diretorio), path.resolve(caminhoAbsoluto));
|
|
52
|
+
return relativo === "" || (!relativo.startsWith("..") && !path.isAbsolute(relativo));
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
// Simbolo declarado cujo dono ancora codigo em arquivo existente fora dos
|
|
56
|
+
// diretoriosCodigo: casa o caminho do simbolo com o diretorio do arquivo.
|
|
57
|
+
function escolherAncoraForaDoEscopoPorSimbolo(caminhos, baseProjeto, simbolo) {
|
|
58
|
+
const simboloComoCaminho = simbolo.toLowerCase().replaceAll(".", "/");
|
|
59
|
+
const porPrefixo = caminhos.find((caminho) => {
|
|
60
|
+
const diretorioRelativo = path.relative(path.resolve(baseProjeto), path.dirname(path.resolve(caminho))).replace(/\\/g, "/").toLowerCase();
|
|
61
|
+
return diretorioRelativo !== "" && simboloComoCaminho.startsWith(`${diretorioRelativo}/`);
|
|
62
|
+
});
|
|
63
|
+
if (porPrefixo) {
|
|
64
|
+
return porPrefixo;
|
|
65
|
+
}
|
|
66
|
+
return caminhos.length === 1 ? caminhos[0] : undefined;
|
|
67
|
+
}
|
|
68
|
+
// O analisador de modulos e sincrono: a existencia em disco dos caminhos
|
|
69
|
+
// declarados em vinculos e sondada antes, pelo chamador, e injetada no estado.
|
|
70
|
+
export function coletarCaminhosDeclaradosVinculosDrift(contexto) {
|
|
71
|
+
const caminhos = [];
|
|
72
|
+
for (const item of contexto.modulosSelecionados) {
|
|
73
|
+
const ir = item.resultado.ir;
|
|
74
|
+
if (!ir) {
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
for (const itemVinculo of coletarVinculosIr(ir)) {
|
|
78
|
+
const declarado = caminhoDeclaradoTentadoVinculo(itemVinculo.vinculo);
|
|
79
|
+
if (declarado) {
|
|
80
|
+
caminhos.push(path.resolve(contexto.baseProjeto, declarado));
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return caminhos;
|
|
85
|
+
}
|
|
33
86
|
export function analisarModulosSelecionadosDrift(estado) {
|
|
34
|
-
const { contexto, mapaImpl, todosSimbolos, mapaRecursos, todosRecursos, todasRotasIndexadas, todosArquivosConhecidos, implsValidos, implsQuebrados, vinculosValidos, vinculosQuebrados, rotasDivergentes, recursosValidos, recursosDivergentes, diagnosticos, tasksResumo, taskPorChave, guardrailsPorTask, resumoVinculosPorTask, arquivosAncoraHerdadosPorTask, } = estado;
|
|
87
|
+
const { contexto, mapaImpl, todosSimbolos, mapaRecursos, todosRecursos, todasRotasIndexadas, todosArquivosConhecidos, implsValidos, implsQuebrados, vinculosValidos, vinculosQuebrados, vinculosForaDoEscopo, arquivosDeclaradosExistentes, rotasDivergentes, recursosValidos, recursosDivergentes, diagnosticos, tasksResumo, taskPorChave, guardrailsPorTask, resumoVinculosPorTask, arquivosAncoraHerdadosPorTask, } = estado;
|
|
35
88
|
for (const item of contexto.modulosSelecionados) {
|
|
36
89
|
const ir = item.resultado.ir;
|
|
37
90
|
if (!ir) {
|
|
@@ -309,6 +362,23 @@ export function analisarModulosSelecionadosDrift(estado) {
|
|
|
309
362
|
});
|
|
310
363
|
}
|
|
311
364
|
}
|
|
365
|
+
const arquivosForaDoEscopoPorDono = new Map();
|
|
366
|
+
for (const itemVinculoDono of coletarVinculosIr(ir)) {
|
|
367
|
+
const declarado = itemVinculoDono.vinculo.arquivo
|
|
368
|
+
?? (itemVinculoDono.vinculo.tipo === "arquivo" ? itemVinculoDono.vinculo.valor : undefined);
|
|
369
|
+
if (!declarado) {
|
|
370
|
+
continue;
|
|
371
|
+
}
|
|
372
|
+
const absoluto = path.resolve(contexto.baseProjeto, declarado);
|
|
373
|
+
if (arquivosDeclaradosExistentes.get(chaveCaminhoCanonicoDrift(absoluto)) !== true
|
|
374
|
+
|| arquivoDentroDoEscopoCodigo(contexto, absoluto)) {
|
|
375
|
+
continue;
|
|
376
|
+
}
|
|
377
|
+
const chaveDono = `${itemVinculoDono.donoTipo}:${itemVinculoDono.dono}`;
|
|
378
|
+
const lista = arquivosForaDoEscopoPorDono.get(chaveDono) ?? [];
|
|
379
|
+
lista.push(absoluto);
|
|
380
|
+
arquivosForaDoEscopoPorDono.set(chaveDono, lista);
|
|
381
|
+
}
|
|
312
382
|
for (const itemVinculo of coletarVinculosIr(ir)) {
|
|
313
383
|
const registro = {
|
|
314
384
|
modulo: ir.nome,
|
|
@@ -385,13 +455,41 @@ export function analisarModulosSelecionadosDrift(estado) {
|
|
|
385
455
|
}
|
|
386
456
|
}
|
|
387
457
|
if (registro.status === "nao_encontrado") {
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
458
|
+
const caminhoTentado = caminhoDeclaradoTentadoVinculo(itemVinculo.vinculo);
|
|
459
|
+
const caminhoAbsoluto = caminhoTentado ? path.resolve(contexto.baseProjeto, caminhoTentado) : undefined;
|
|
460
|
+
const existeNoDisco = caminhoAbsoluto !== undefined
|
|
461
|
+
&& arquivosDeclaradosExistentes.get(chaveCaminhoCanonicoDrift(caminhoAbsoluto)) === true;
|
|
462
|
+
// Arquivo proprio declarado fora do escopo, ou simbolo que herda a ancora
|
|
463
|
+
// de arquivo declarado do mesmo dono existente fora dos diretoriosCodigo.
|
|
464
|
+
const alvoForaDoEscopo = existeNoDisco && caminhoAbsoluto !== undefined && !arquivoDentroDoEscopoCodigo(contexto, caminhoAbsoluto)
|
|
465
|
+
? caminhoAbsoluto
|
|
466
|
+
: simboloDeclarado
|
|
467
|
+
? escolherAncoraForaDoEscopoPorSimbolo(arquivosForaDoEscopoPorDono.get(`${itemVinculo.donoTipo}:${itemVinculo.dono}`) ?? [], contexto.baseProjeto, simboloDeclarado)
|
|
468
|
+
: undefined;
|
|
469
|
+
if (alvoForaDoEscopo) {
|
|
470
|
+
const diretorioSugerido = sugerirDiretorioCodigoDrift(contexto.baseProjeto, alvoForaDoEscopo);
|
|
471
|
+
registro.status = "fora_do_escopo";
|
|
472
|
+
registro.confianca = "baixa";
|
|
473
|
+
registro.arquivo = alvoForaDoEscopo;
|
|
474
|
+
registro.diretorioSugerido = diretorioSugerido;
|
|
475
|
+
vinculosForaDoEscopo.push(registro);
|
|
476
|
+
diagnosticos.push({
|
|
477
|
+
tipo: "vinculo_fora_do_escopo",
|
|
478
|
+
modulo: ir.nome,
|
|
479
|
+
severidade: "aviso",
|
|
480
|
+
mensagem: `Vinculo ${registro.tipo}="${registro.valor}" de ${itemVinculo.donoTipo} "${itemVinculo.dono}": o codigo existe no workspace mas esta fora dos diretoriosCodigo do sema.config.json; adicione "${diretorioSugerido}" a diretoriosCodigo para o drift verificar este vinculo.`,
|
|
481
|
+
...(itemVinculo.donoTipo === "task" ? { task: itemVinculo.dono } : itemVinculo.donoTipo === "route" ? { route: itemVinculo.dono } : {}),
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
else {
|
|
485
|
+
vinculosQuebrados.push(registro);
|
|
486
|
+
diagnosticos.push({
|
|
487
|
+
tipo: "vinculo_quebrado",
|
|
488
|
+
modulo: ir.nome,
|
|
489
|
+
mensagem: `Vinculo ${registro.tipo}="${registro.valor}" de ${itemVinculo.donoTipo} "${itemVinculo.dono}" nao foi resolvido no codigo vivo.`,
|
|
490
|
+
...(itemVinculo.donoTipo === "task" ? { task: itemVinculo.dono } : itemVinculo.donoTipo === "route" ? { route: itemVinculo.dono } : {}),
|
|
491
|
+
});
|
|
492
|
+
}
|
|
395
493
|
}
|
|
396
494
|
else {
|
|
397
495
|
vinculosValidos.push(registro);
|
|
@@ -429,7 +527,7 @@ export function analisarModulosSelecionadosDrift(estado) {
|
|
|
429
527
|
if (registro.status === "nao_encontrado") {
|
|
430
528
|
resumo.quebrados += 1;
|
|
431
529
|
}
|
|
432
|
-
else {
|
|
530
|
+
else if (registro.status !== "fora_do_escopo") {
|
|
433
531
|
resumo.validos += 1;
|
|
434
532
|
}
|
|
435
533
|
if (registro.arquivo) {
|
package/dist/driftEscopo.d.ts
CHANGED
|
@@ -18,5 +18,6 @@ export interface PlanoEscopoDrift {
|
|
|
18
18
|
bloqueios: string[];
|
|
19
19
|
dependencias: Record<string, string[]>;
|
|
20
20
|
}
|
|
21
|
+
export declare function sondarCaminhosDeclaradosExistentes(caminhos: readonly string[], baseProjeto: string): Promise<Map<string, boolean>>;
|
|
21
22
|
export declare function planejarEscopoDrift(contexto: ContextoProjetoCarregado, configuracao: ConfiguracaoEscopoDriftAplicada): Promise<PlanoEscopoDrift>;
|
|
22
23
|
export declare function expandirDependenciasPlanoDrift(contexto: ContextoProjetoCarregado, plano: PlanoEscopoDrift, catalogo: CatalogoDrift): Promise<PlanoEscopoDrift>;
|
package/dist/driftEscopo.js
CHANGED
|
@@ -84,6 +84,12 @@ async function sondarArquivosRegularesContidos(caminhos, raizReal) {
|
|
|
84
84
|
const resultados = await mapearComLimite(unicos, CONCORRENCIA_SONDAGENS_DRIFT, async ([chave, caminho]) => [chave, await arquivoRegularContido(caminho, raizReal)]);
|
|
85
85
|
return new Map(resultados);
|
|
86
86
|
}
|
|
87
|
+
// Sonda arquivos declarados em vinculos contra o workspace real: alimenta a
|
|
88
|
+
// distincao entre "arquivo nao existe" e "existe fora dos diretoriosCodigo".
|
|
89
|
+
export async function sondarCaminhosDeclaradosExistentes(caminhos, baseProjeto) {
|
|
90
|
+
const raizReal = await realpath(baseProjeto);
|
|
91
|
+
return sondarArquivosRegularesContidos(caminhos, raizReal);
|
|
92
|
+
}
|
|
87
93
|
function vinculosModulo(ir) {
|
|
88
94
|
return [
|
|
89
95
|
...ir.vinculos,
|
|
@@ -570,7 +576,12 @@ async function resolverReferenciasLocais(contexto, arquivoOrigem, referencias, c
|
|
|
570
576
|
}
|
|
571
577
|
const modulo = item.referencia.slice("python:".length);
|
|
572
578
|
const prefixoLocalResolvido = modulosPythonResolvidos.some((prefixo) => prefixo !== modulo && modulo.startsWith(`${prefixo}.`));
|
|
573
|
-
if (
|
|
579
|
+
if (prefixoLocalResolvido) {
|
|
580
|
+
// Em `from modulo import nome`, `nome` pode ser simbolo exportado ou submodulo.
|
|
581
|
+
// Inclua o submodulo quando existir, mas sua ausencia nao prova dependencia quebrada.
|
|
582
|
+
return { referencia: item.referencia };
|
|
583
|
+
}
|
|
584
|
+
if (!referenciaPythonEhLocalObrigatoria(item.referencia)) {
|
|
574
585
|
return item;
|
|
575
586
|
}
|
|
576
587
|
const candidatos = candidatosReferenciaLocalDrift(contexto, arquivoOrigem, item.referencia);
|
package/dist/index.part03.d.ts
CHANGED
|
@@ -13,6 +13,7 @@ export declare function resumirDriftPorModulo(modulo: string | null, caminho: st
|
|
|
13
13
|
implsQuebrados: number;
|
|
14
14
|
vinculosValidos: number;
|
|
15
15
|
vinculosQuebrados: number;
|
|
16
|
+
vinculosForaDoEscopo: number;
|
|
16
17
|
recursosValidos: number;
|
|
17
18
|
recursosDivergentesCount: number;
|
|
18
19
|
tasksSemImplementacao: number;
|
|
@@ -31,6 +32,7 @@ export declare function resumirDriftPorModulo(modulo: string | null, caminho: st
|
|
|
31
32
|
rotasDivergentes: import("./drift.part01.js").RegistroRotaDivergente[];
|
|
32
33
|
recursosDivergentes: import("./drift.part01.js").RegistroRecursoDrift[];
|
|
33
34
|
vinculosQuebradosDetalhes: import("./drift.part01.js").RegistroVinculoDrift[];
|
|
35
|
+
vinculosForaDoEscopoDetalhes: import("./drift.part01.js").RegistroVinculoDrift[];
|
|
34
36
|
};
|
|
35
37
|
export declare function criarBriefingAgente(arquivo: string, modulo: string, ir: IrModulo | null, resumoDrift: ReturnType<typeof resumirDriftPorModulo>, resultadoDrift: Awaited<ReturnType<typeof analisarDriftLegado>>): {
|
|
36
38
|
arquivo: string;
|
package/dist/index.part03.js
CHANGED
|
@@ -112,6 +112,9 @@ export function resumirDriftPorModulo(modulo, caminho, resultadoDrift) {
|
|
|
112
112
|
const vinculosQuebrados = modulo
|
|
113
113
|
? resultadoDrift.vinculos_quebrados.filter((vinculo) => vinculo.modulo === modulo)
|
|
114
114
|
: [];
|
|
115
|
+
const vinculosForaDoEscopo = modulo
|
|
116
|
+
? resultadoDrift.vinculos_fora_do_escopo.filter((vinculo) => vinculo.modulo === modulo)
|
|
117
|
+
: [];
|
|
115
118
|
const rotasDivergentes = modulo
|
|
116
119
|
? resultadoDrift.rotas_divergentes.filter((rota) => rota.modulo === modulo)
|
|
117
120
|
: [];
|
|
@@ -136,6 +139,7 @@ export function resumirDriftPorModulo(modulo, caminho, resultadoDrift) {
|
|
|
136
139
|
...implsValidos.map((impl) => impl.arquivo).filter((item) => Boolean(item)),
|
|
137
140
|
...implsQuebrados.flatMap((impl) => impl.candidatos?.map((candidato) => candidato.arquivo) ?? []),
|
|
138
141
|
...vinculosValidos.map((vinculo) => vinculo.arquivo).filter((item) => Boolean(item)),
|
|
142
|
+
...vinculosForaDoEscopo.map((vinculo) => vinculo.arquivo).filter((item) => Boolean(item)),
|
|
139
143
|
...recursosValidos.map((recurso) => recurso.arquivo).filter(Boolean),
|
|
140
144
|
...recursosDivergentes.map((recurso) => recurso.arquivo).filter(Boolean),
|
|
141
145
|
])].sort((a, b) => a.localeCompare(b, "pt-BR"));
|
|
@@ -176,6 +180,7 @@ export function resumirDriftPorModulo(modulo, caminho, resultadoDrift) {
|
|
|
176
180
|
implsQuebrados: implsQuebrados.length,
|
|
177
181
|
vinculosValidos: vinculosValidos.length,
|
|
178
182
|
vinculosQuebrados: vinculosQuebrados.length,
|
|
183
|
+
vinculosForaDoEscopo: vinculosForaDoEscopo.length,
|
|
179
184
|
recursosValidos: recursosValidos.length,
|
|
180
185
|
recursosDivergentesCount: recursosDivergentes.length,
|
|
181
186
|
tasksSemImplementacao: tasks.filter((task) => task.semImplementacao).length,
|
|
@@ -198,6 +203,7 @@ export function resumirDriftPorModulo(modulo, caminho, resultadoDrift) {
|
|
|
198
203
|
rotasDivergentes,
|
|
199
204
|
recursosDivergentes,
|
|
200
205
|
vinculosQuebradosDetalhes: vinculosQuebrados,
|
|
206
|
+
vinculosForaDoEscopoDetalhes: vinculosForaDoEscopo,
|
|
201
207
|
};
|
|
202
208
|
}
|
|
203
209
|
export function criarBriefingAgente(arquivo, modulo, ir, resumoDrift, resultadoDrift) {
|
package/dist/index.part05.js
CHANGED
|
@@ -173,6 +173,7 @@ export async function comandoDrift(entrada, args, emJson, cwd = process.cwd()) {
|
|
|
173
173
|
console.log(`- Impl quebrados: ${resultado.impls_quebrados.length}`);
|
|
174
174
|
console.log(`- Vinculos validos: ${resultado.vinculos_validos.length}`);
|
|
175
175
|
console.log(`- Vinculos quebrados: ${resultado.vinculos_quebrados.length}`);
|
|
176
|
+
console.log(`- Vinculos fora do escopo (informativo): ${resultado.vinculos_fora_do_escopo.length}`);
|
|
176
177
|
console.log(`- Rotas divergentes: ${resultado.rotas_divergentes.length}`);
|
|
177
178
|
console.log(`- Recursos vivos validos: ${resultado.recursos_validos.length}`);
|
|
178
179
|
console.log(`- Recursos vivos divergentes: ${resultado.recursos_divergentes.length}`);
|
|
@@ -229,6 +230,12 @@ export async function comandoDrift(entrada, args, emJson, cwd = process.cwd()) {
|
|
|
229
230
|
console.log(` - ${vinculo.modulo}.${vinculo.dono} :: ${vinculo.tipo}=${vinculo.valor}`);
|
|
230
231
|
}
|
|
231
232
|
}
|
|
233
|
+
if (resultado.vinculos_fora_do_escopo.length > 0) {
|
|
234
|
+
console.log("- Vinculos fora do escopo (arquivo existe fora dos diretoriosCodigo; informativo):");
|
|
235
|
+
for (const vinculo of resultado.vinculos_fora_do_escopo) {
|
|
236
|
+
console.log(` - ${vinculo.modulo}.${vinculo.dono} :: ${vinculo.tipo}=${vinculo.valor} :: adicione "${vinculo.diretorioSugerido ?? "./"}" a diretoriosCodigo`);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
232
239
|
if (resultado.resumo_operacional.oQueTocar.length > 0) {
|
|
233
240
|
console.log("- O que tocar primeiro:");
|
|
234
241
|
for (const alvo of resultado.resumo_operacional.oQueTocar.slice(0, 8)) {
|
package/dist/initCommand.js
CHANGED
|
@@ -1,11 +1,35 @@
|
|
|
1
1
|
// SEMA-GOVERNED: sema.produto.cli_init_templates, sema.produto.escrita_segura_workspace
|
|
2
2
|
// Descrição: inicializa projetos preservando arquivos existentes e bloqueando escapes do workspace.
|
|
3
3
|
import * as fs from "node:fs";
|
|
4
|
+
import { readFile } from "node:fs/promises";
|
|
4
5
|
import path from "node:path";
|
|
5
6
|
import { escreverArquivos } from "./fsGovernado.js";
|
|
6
7
|
import { arquivosTemplateIniciar } from "./initTemplatesBase.js";
|
|
7
8
|
import { materializarExemplosOficiais, normalizarCaminhoExemplo, planejarExemplosOficiais } from "./exemplosOficiais.js";
|
|
8
|
-
import {
|
|
9
|
+
import { carregarConfiguracaoProjeto } from "./projetoConfig.js";
|
|
10
|
+
import { escreverArquivoWorkspaceSeguro, validarDestinosEscritaWorkspace } from "./workspaceWrite.js";
|
|
11
|
+
async function registrarSubpastaNoConfigAncestral(caminhoConfig, subpastaAbsoluta) {
|
|
12
|
+
const basePai = path.dirname(caminhoConfig);
|
|
13
|
+
const relativoSubpasta = `./${path.relative(basePai, subpastaAbsoluta).replace(/\\/g, "/")}`;
|
|
14
|
+
try {
|
|
15
|
+
const configPai = JSON.parse(await readFile(caminhoConfig, "utf8"));
|
|
16
|
+
const diretoriosAtuais = Array.isArray(configPai.diretoriosCodigo)
|
|
17
|
+
? configPai.diretoriosCodigo.filter((valor) => typeof valor === "string")
|
|
18
|
+
: [];
|
|
19
|
+
const jaRegistrado = diretoriosAtuais.some((diretorio) => diretorio.replace(/\\/g, "/").toLowerCase() === relativoSubpasta.toLowerCase());
|
|
20
|
+
if (jaRegistrado) {
|
|
21
|
+
console.log(`Subpasta ${relativoSubpasta} ja consta em diretoriosCodigo de ${caminhoConfig}.`);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
configPai.diretoriosCodigo = [...diretoriosAtuais, relativoSubpasta];
|
|
25
|
+
await escreverArquivoWorkspaceSeguro(basePai, "sema.config.json", `${JSON.stringify(configPai, null, 2)}\n`, { sobrescrever: true });
|
|
26
|
+
console.log(`Subpasta ${relativoSubpasta} registrada em diretoriosCodigo de ${caminhoConfig}.`);
|
|
27
|
+
}
|
|
28
|
+
catch (erro) {
|
|
29
|
+
console.error(`Nao foi possivel registrar a subpasta no config do workspace pai: ${erro instanceof Error ? erro.message : String(erro)}`);
|
|
30
|
+
console.error(`Adicione "${relativoSubpasta}" a diretoriosCodigo em ${caminhoConfig} para o drift cobrir esta subpasta.`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
9
33
|
async function sincronizarKitIaInicial(cwd) {
|
|
10
34
|
const [{ gerarResumoProjetoIa }, { sincronizarEntrypointCodex }] = await Promise.all([
|
|
11
35
|
import("./index.part04.js"),
|
|
@@ -33,7 +57,19 @@ const EXEMPLOS_STARTER_INICIAR = new Set([
|
|
|
33
57
|
"exemplos/autenticacao.sema",
|
|
34
58
|
]);
|
|
35
59
|
export async function comandoIniciar(cwd, template, opcoes = {}) {
|
|
36
|
-
const
|
|
60
|
+
const arquivosCompletos = arquivosTemplateIniciar(template);
|
|
61
|
+
const configAncestral = await carregarConfiguracaoProjeto(cwd);
|
|
62
|
+
const subpastaGovernada = Boolean(configAncestral
|
|
63
|
+
&& path.resolve(configAncestral.baseDiretorio) !== path.resolve(cwd));
|
|
64
|
+
// Em subpasta de workspace ja governado o config do template e omitido: a
|
|
65
|
+
// subpasta e registrada nos diretoriosCodigo do sema.config.json ancestral
|
|
66
|
+
// e o lookup de workspace continua subindo para o pai.
|
|
67
|
+
const arquivos = subpastaGovernada && configAncestral
|
|
68
|
+
? arquivosCompletos.filter((arquivo) => arquivo.caminhoRelativo !== "sema.config.json")
|
|
69
|
+
: arquivosCompletos;
|
|
70
|
+
if (subpastaGovernada && configAncestral) {
|
|
71
|
+
await registrarSubpastaNoConfigAncestral(configAncestral.caminho, path.resolve(cwd));
|
|
72
|
+
}
|
|
37
73
|
const planoExemplos = await planejarExemplosOficiais();
|
|
38
74
|
if (!planoExemplos.origem) {
|
|
39
75
|
console.error("Diretorio de exemplos oficiais nao foi encontrado no pacote da CLI.");
|
package/docs/ai-workflow.md
CHANGED
|
@@ -18,9 +18,9 @@ This is the minimum workflow for Codex in a local workspace.
|
|
|
18
18
|
|
|
19
19
|
Contract edit rule: `.sema` has its own size budget. Above 300 lines, plan a split by domain/capability; above 500, do not create or edit before splitting. Do not use parte_1/parte_2 and do not force a 1:1 contract-to-file relationship; several contracts can govern the same file through `vinculos`.
|
|
20
20
|
|
|
21
|
-
Closing rule: the `payload` of `sema drift --cache fresh --json` must return `sucesso:true`. If it reports `sucesso:false`, `vinculos_quebrados`, `rotas_divergentes`, or `impls_quebrados`, the task is still blocked. Passing unit tests or a cache hit do not replace fresh green drift. In 3.0.0, `ok` describes the CLI result path and never replaces a command-specific domain verdict.
|
|
21
|
+
Closing rule: the `payload` of `sema drift --cache fresh --json` must return `sucesso:true`. If it reports `sucesso:false`, `vinculos_quebrados`, `rotas_divergentes`, or `impls_quebrados`, the task is still blocked. Passing unit tests or a cache hit do not replace fresh green drift. `vinculos_fora_do_escopo` is informational: the file exists in the workspace outside the configured `diretoriosCodigo`, and each entry carries a `diretorioSugerido` to add to `sema.config.json`; it does not block closure. In 3.0.0, `ok` describes the CLI result path and never replaces a command-specific domain verdict.
|
|
22
22
|
|
|
23
|
-
Focused drift rule: file and module scopes plan their physical file set before cataloging. They never fall back to a whole-project walk when no safe anchor exists. All indexers and semantic-budget checks reuse the same in-memory source read; a global walk is reserved for explicit `--escopo projeto`. Code discovery is deferred until that plan exists, homonymous candidates fail as ambiguous, and configured contract origins or code roots resolving outside the workspace are rejected before enumeration. `inspecionar`, `impacto`, and `renomear-semantico` preserve the same directed boundary. Missing local dependencies make coverage partial and block success.
|
|
23
|
+
Focused drift rule: file and module scopes plan their physical file set before cataloging. They never fall back to a whole-project walk when no safe anchor exists. All indexers and semantic-budget checks reuse the same in-memory source read; a global walk is reserved for explicit `--escopo projeto`. Code discovery is deferred until that plan exists, homonymous candidates fail as ambiguous, and configured contract origins or code roots resolving outside the workspace are rejected before enumeration. `inspecionar`, `impacto`, and `renomear-semantico` preserve the same directed boundary. Missing unambiguous local dependencies make coverage partial and block success. In Python, an unresolved `name` from `from module import name` is not itself a missing file after `module` resolves: it may be an exported symbol, while a real child module is still included whenever its file exists.
|
|
24
24
|
|
|
25
25
|
Cache rule: a closure gate should use `sema drift <contrato> --escopo modulo --cache fresh --json`. `--cache none` executes the same analysis without persistent-cache I/O, while `--cache cache` may reuse only extraction data whose schema, workspace identity, Git HEAD, plan, configuration, contracts, member paths, and strong content digests all match. Cache objects are stored in the operating system's user-cache directory, never below the workspace. A hit is acceleration, not evidence: final links, diagnostics, score, and success are always recalculated. Corruption or an unavailable cache degrades to an in-memory calculation.
|
|
26
26
|
|
package/docs/commands.md
CHANGED
|
@@ -31,7 +31,7 @@ Then read every required doc returned by `docs-impacto`.
|
|
|
31
31
|
|
|
32
32
|
## Contract and Discovery
|
|
33
33
|
|
|
34
|
-
- `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]`: creates a new Sema project and preserves existing files by default; `--force` is the only explicit overwrite path.
|
|
34
|
+
- `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]`: creates a new Sema project and preserves existing files by default; `--force` is the only explicit overwrite path. Inside a subfolder of an already governed workspace, the subfolder is registered under `diretoriosCodigo` of the ancestral `sema.config.json` (safe write, deduplicated) instead of creating a local config; if the parent config cannot be written, the scaffold proceeds and prints the exact line to add.
|
|
35
35
|
- `sema validar <arquivo-ou-pasta> --json`: validates `.sema` contracts.
|
|
36
36
|
- `sema diagnosticos <arquivo.sema> --json`: details errors and warnings.
|
|
37
37
|
- `sema formatar <arquivo-ou-pasta>`: formats contracts.
|
|
@@ -51,9 +51,9 @@ Then read every required doc returned by `docs-impacto`.
|
|
|
51
51
|
- `sema verificar <arquivo-ou-pasta> --json`: runs aggregated final verification.
|
|
52
52
|
- `sema finalizar-mudanca --intencao "<acao>" --doc-lida <arquivo> --json`: proves documentation reading before closure.
|
|
53
53
|
|
|
54
|
-
Honest closure: unwrap drift JSON and treat its command payload as the source of truth. `payload.sucesso:false`, non-empty `payload.vinculos_quebrados`, non-empty `payload.rotas_divergentes`, or non-empty `payload.impls_quebrados` mean the change is not complete yet. Do not report "clean drift" without a green payload.
|
|
54
|
+
Honest closure: unwrap drift JSON and treat its command payload as the source of truth. `payload.sucesso:false`, non-empty `payload.vinculos_quebrados`, non-empty `payload.rotas_divergentes`, or non-empty `payload.impls_quebrados` mean the change is not complete yet. Do not report "clean drift" without a green payload. `payload.vinculos_fora_do_escopo` is informational: the file exists in the workspace outside `diretoriosCodigo`, and each entry carries a `diretorioSugerido` for `sema.config.json`.
|
|
55
55
|
|
|
56
|
-
Focused drift exposes its planned, declared, inferred, and missing files plus catalog visit/read metrics in `escopo_aplicado`. A missing declared file or a missing code-referenced dependency is a blocker reported as `vinculo_quebrado`; a missing inferred candidate derived from an `impl` symbol is informational under `arquivosAusentesInferidos` and never blocks an implementation that resolved. File and module scopes fail closed without a safe anchor, with homonymous implementation candidates, or with missing local dependencies; only `--escopo projeto` may walk every configured code root. Logical roots such as `src` are probed deterministically without a discovery walk. Configured contract origins and code roots are confined before enumeration, and `inspecionar`, `impacto`, and `renomear-semantico` reuse the same directed boundary without reopening arbitrary external paths.
|
|
56
|
+
Focused drift exposes its planned, declared, inferred, and missing files plus catalog visit/read metrics in `escopo_aplicado`. A missing declared file or a missing unambiguous code-referenced dependency is a blocker reported as `vinculo_quebrado`; a missing inferred candidate derived from an `impl` symbol is informational under `arquivosAusentesInferidos` and never blocks an implementation that resolved. For Python `from module import name`, Sema still includes `name` when it resolves as a real submodule, but never reports the absent submodule candidate as a broken link after the parent module resolved because `name` may be a function, class, or variable. File and module scopes fail closed without a safe anchor, with homonymous implementation candidates, or with missing local dependencies; only `--escopo projeto` may walk every configured code root. Logical roots such as `src` are probed deterministically without a discovery walk. Configured contract origins and code roots are confined before enumeration, and `inspecionar`, `impacto`, and `renomear-semantico` reuse the same directed boundary without reopening arbitrary external paths.
|
|
57
57
|
|
|
58
58
|
Drift analysis and cache modes are explicit:
|
|
59
59
|
|
|
@@ -156,7 +156,7 @@ Spatial model and render mode are orthogonal: `THREE_D + HEADLESS` is valid, whi
|
|
|
156
156
|
- Do not stop after `sema compilar` if the contract target files still do not exist.
|
|
157
157
|
- Do not replace `sema compilar` with `sema testar` when the contract requires generated code.
|
|
158
158
|
- Do not create a Markdown report to pretend a gate ran.
|
|
159
|
-
- Do not say drift passed when the unwrapped `sema drift --cache fresh --json` payload returned `sucesso:false`, a non-empty `vinculos_quebrados`, `rotas_divergentes`, or `impls_quebrados` list.
|
|
159
|
+
- Do not say drift passed when the unwrapped `sema drift --cache fresh --json` payload returned `sucesso:false`, a non-empty `vinculos_quebrados`, `rotas_divergentes`, or `impls_quebrados` list. `vinculos_fora_do_escopo` entries are informational scope hints, not broken links.
|
|
160
160
|
- Do not declare a UI responsive without mobile/desktop proof; horizontal scroll at 390px blocks closure.
|
|
161
161
|
|
|
162
162
|
Governed code policy: keep the `SEMA-GOVERNED` marker, split large code by real responsibility, preserve contract links, and never treat a generated output directory as the final delivery.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@semacode/cli",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.13.1",
|
|
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",
|