@orkastery/cli 0.5.0 → 0.5.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/adapters/claude-code/README.md +11 -2
- package/adapters/claude-code/commands/onboarding.md +3 -2
- package/adapters/openclaw/README.md +11 -0
- package/adapters/openclaw/dist/index.js +1 -1
- package/adapters/openclaw/openclaw.plugin.json +1 -1
- package/adapters/openclaw/package.json +1 -1
- package/adapters/openclaw/src/index.ts +1 -1
- package/dist/adapters/claude-bg.js +20 -6
- package/dist/auditrun.js +1 -1
- package/dist/claim-lint.js +3 -1
- package/dist/docs.js +71 -3
- package/dist/doctor.js +209 -32
- package/dist/entrega-pr.js +7 -0
- package/dist/fabrica-estado.js +21 -3
- package/dist/fix.js +3 -3
- package/dist/hitl-resumo.js +24 -1
- package/dist/hosts.js +7 -3
- package/dist/index.js +42 -14
- package/dist/init.js +11 -5
- package/dist/integracoes-locais.js +2 -0
- package/dist/intelligence-graph-cli.js +181 -27
- package/dist/intelligence-graph-contract.js +71 -12
- package/dist/intelligence-graph-extract-md.js +41 -23
- package/dist/intelligence-graph-extract-ts.js +181 -11
- package/dist/intelligence-graph-extract.js +186 -9
- package/dist/intelligence-graph-index.js +205 -24
- package/dist/intelligence-graph-repo.js +10 -0
- package/dist/leases.js +16 -0
- package/dist/ledger.js +9 -0
- package/dist/licoes.js +5 -4
- package/dist/liveness.js +2 -1
- package/dist/maestro-runtime.js +3 -0
- package/dist/manifest.js +24 -0
- package/dist/mcp-artifacts.js +2 -0
- package/dist/mcp-git.js +60 -12
- package/dist/mcp-grafo-worker.js +91 -0
- package/dist/mcp-grafo.js +233 -0
- package/dist/mcp-install.js +2 -1
- package/dist/mcp-server.js +9 -0
- package/dist/mcp-ship.js +2 -0
- package/dist/mcp-verify.js +3 -1
- package/dist/network-roadmap.js +5 -5
- package/dist/ocupacao.js +6 -2
- package/dist/onboarding.js +9 -4
- package/dist/orquestracao.js +2 -1
- package/dist/parado-no-condutor.js +933 -0
- package/dist/policies.js +27 -2
- package/dist/prova-ativacao.js +345 -0
- package/dist/pulse-delivery.js +27 -6
- package/dist/pulse.js +53 -3
- package/dist/roadmap-status.js +142 -7
- package/dist/runtime-context.js +5 -1
- package/dist/sessoes-inventario.js +7 -1
- package/dist/sessoes.js +4 -1
- package/dist/ship.js +25 -1
- package/dist/thread.js +21 -2
- package/dist/util.js +71 -4
- package/dist/verify.js +2 -2
- package/dist/worktree.js +29 -6
- package/eval/casos/onboarding.json +1 -1
- package/package.json +1 -1
- package/skills/core/onboarding/SKILL.md +3 -2
- package/skills/phases/ship-release/SKILL.md +6 -3
|
@@ -1,7 +1,33 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.OPCOES_TS_DESCRITAS = void 0;
|
|
4
|
+
exports.casaSonda = casaSonda;
|
|
4
5
|
exports.extrairTypeScript = extrairTypeScript;
|
|
6
|
+
/** Extensoes que o extrator TS le; as do `extensaoDe` do KG2. */
|
|
7
|
+
const EXTENSOES_DO_PROGRAMA = ['.cjs', '.cts', '.js', '.jsx', '.mjs', '.mts', '.ts', '.tsx'];
|
|
8
|
+
const DECLARACOES_TS = ['.d.ts', '.d.mts', '.d.cts'];
|
|
9
|
+
/** A base sem a extensao (o TypeScript troca `.js` por `.ts`, e `.d.ts` conta inteira). */
|
|
10
|
+
function semExtensao(base) {
|
|
11
|
+
const nome = base.slice(base.lastIndexOf('/') + 1);
|
|
12
|
+
for (const d of DECLARACOES_TS)
|
|
13
|
+
if (nome.endsWith(d) && nome.length > d.length)
|
|
14
|
+
return base.slice(0, -d.length);
|
|
15
|
+
const i = nome.lastIndexOf('.');
|
|
16
|
+
return i <= 0 ? base : base.slice(0, base.length - (nome.length - i));
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* RM-031 KG4 (D3): o caminho `p`, novo ou removido, pode mudar uma resolucao de base `base`? O
|
|
20
|
+
* TypeScript e o Node so tentam a base exata, a base com extensao, a base como pasta (`index`,
|
|
21
|
+
* `package.json`) e o mesmo sem a extensao; base vazia (a raiz) casa tudo.
|
|
22
|
+
*/
|
|
23
|
+
function casaSonda(base, p) {
|
|
24
|
+
if (base === '')
|
|
25
|
+
return true;
|
|
26
|
+
if (p === base || p.startsWith(`${base}.`) || p.startsWith(`${base}/`))
|
|
27
|
+
return true;
|
|
28
|
+
const sem = semExtensao(base);
|
|
29
|
+
return sem !== base && (p === sem || p.startsWith(`${sem}.`) || p.startsWith(`${sem}/`));
|
|
30
|
+
}
|
|
5
31
|
/** D2: opcoes fixas do compilador, descritas por nome para entrar no `config_hash`. */
|
|
6
32
|
exports.OPCOES_TS_DESCRITAS = Object.freeze({
|
|
7
33
|
allowJs: true, checkJs: false, esModuleInterop: true, jsx: 'preserve', module: 'commonjs', moduleResolution: 'node10',
|
|
@@ -68,7 +94,12 @@ function caminhoLiteral(de, especificador) {
|
|
|
68
94
|
}
|
|
69
95
|
return r.join('/');
|
|
70
96
|
}
|
|
71
|
-
|
|
97
|
+
/**
|
|
98
|
+
* RM-031 KG4 (D3): modulo vazio fora da raiz virtual (nenhum caminho do manifesto o nomeia, e nada o
|
|
99
|
+
* resolve), so para ler o escopo global do checker sem local que esconda global de mesmo nome.
|
|
100
|
+
*/
|
|
101
|
+
const TEXTO_DO_SINTETICO = 'export {};\n';
|
|
102
|
+
function criarHost(ts, e, sintetico) {
|
|
72
103
|
const arquivos = new Set(e.arquivos), dirs = new Set([e.raiz]);
|
|
73
104
|
for (const p of e.arquivos) {
|
|
74
105
|
const partes = p.split('/');
|
|
@@ -83,6 +114,8 @@ function criarHost(ts, e) {
|
|
|
83
114
|
};
|
|
84
115
|
return {
|
|
85
116
|
getSourceFile: (nome, alvo) => {
|
|
117
|
+
if (nome === sintetico)
|
|
118
|
+
return ts.createSourceFile(nome, TEXTO_DO_SINTETICO, alvo, true);
|
|
86
119
|
const p = dentro(nome), t = p === null ? undefined : e.texto(p);
|
|
87
120
|
return t === undefined ? undefined : ts.createSourceFile(nome, t, alvo, true);
|
|
88
121
|
},
|
|
@@ -109,13 +142,14 @@ function criarHost(ts, e) {
|
|
|
109
142
|
};
|
|
110
143
|
}
|
|
111
144
|
function extrairTypeScript(e, ts) {
|
|
112
|
-
const
|
|
145
|
+
const resultado = { unidades: new Map(), globais: [], faltantes: [] };
|
|
113
146
|
if (e.fontes.length === 0)
|
|
114
|
-
return
|
|
147
|
+
return resultado;
|
|
115
148
|
const absoluto = (p) => `${e.raiz}/${p}`;
|
|
116
149
|
const relativo = (p) => (p.startsWith(`${e.raiz}/`) ? p.slice(e.raiz.length + 1) : null);
|
|
117
|
-
const
|
|
118
|
-
const
|
|
150
|
+
const sintetico = `${e.raiz}-escopo-global.ts`;
|
|
151
|
+
const host = criarHost(ts, e, sintetico), opts = opcoes(ts);
|
|
152
|
+
const programa = ts.createProgram([...e.fontes.map((f) => absoluto(f.path)), sintetico], opts, host);
|
|
119
153
|
const checker = programa.getTypeChecker();
|
|
120
154
|
const cache = ts.createModuleResolutionCache(e.raiz, (p) => p, opts);
|
|
121
155
|
const arquivos = new Set(e.arquivos);
|
|
@@ -473,41 +507,117 @@ function extrairTypeScript(e, ts) {
|
|
|
473
507
|
* lanca erro). Especificador nao relativo vai a node_modules e fica sem alvo. Em ESM o especificador e
|
|
474
508
|
* URL: so liga o que a URL le como o caminho literal (`urlComoCaminho`).
|
|
475
509
|
*/
|
|
510
|
+
/** KG4 (D3): `bruto` e o caminho que a resolucao do Node achou, antes de conferir se ele carrega. */
|
|
476
511
|
function alvoNoNode(de, chave) {
|
|
477
512
|
const esp = especificadorDaChave(chave), formato = formatoDe(de);
|
|
478
513
|
const relativoAoArquivo = esp === '.' || esp === '..' || esp.startsWith('./') || esp.startsWith('../');
|
|
479
514
|
if (!relativoAoArquivo || formato === 'falha')
|
|
480
|
-
return null;
|
|
515
|
+
return { alvo: null, bruto: null };
|
|
481
516
|
let alvo;
|
|
482
517
|
if (chave[0] === 'd' || (chave[0] === 'v' && formato === 'esm'))
|
|
483
518
|
alvo = urlComoCaminho(esp) ? resolverNode(de, esp, true) : null;
|
|
484
519
|
else if (chave[0] === 'r' && formato === 'cjs' && !escopoDe(de).invalido)
|
|
485
520
|
alvo = resolverNode(de, esp, false);
|
|
486
521
|
else
|
|
487
|
-
return null;
|
|
488
|
-
return alvo !== null && carregavel(alvo, chave[0] !== 'r') ? alvo : null;
|
|
522
|
+
return { alvo: null, bruto: null };
|
|
523
|
+
return { alvo: alvo !== null && carregavel(alvo, chave[0] !== 'r') ? alvo : null, bruto: alvo };
|
|
489
524
|
}
|
|
525
|
+
/**
|
|
526
|
+
* KG4 (D3): os nomes de modulo que o TypeScript coleta do arquivo para o programa (import, export,
|
|
527
|
+
* require, import(), tipo importado, inclusive no JSDoc de JavaScript). Sem a lista, nao ha como provar
|
|
528
|
+
* as dependencias e a extracao falha, em vez de reaproveitar sem prova.
|
|
529
|
+
*/
|
|
530
|
+
const nomesDeModulo = (sf) => {
|
|
531
|
+
const imports = sf.imports;
|
|
532
|
+
if (!Array.isArray(imports))
|
|
533
|
+
throw new Error('extracao.interna.typescript-sem-imports');
|
|
534
|
+
return imports.map((n) => n.text);
|
|
535
|
+
};
|
|
536
|
+
/** Caminho relativo a raiz por segmentos; `null` se sobe acima da raiz. */
|
|
537
|
+
const normalizar = (partes) => {
|
|
538
|
+
const r = [];
|
|
539
|
+
for (const p of partes) {
|
|
540
|
+
if (p === '' || p === '.')
|
|
541
|
+
continue;
|
|
542
|
+
if (p === '..') {
|
|
543
|
+
if (!r.length)
|
|
544
|
+
return null;
|
|
545
|
+
r.pop();
|
|
546
|
+
}
|
|
547
|
+
else
|
|
548
|
+
r.push(p);
|
|
549
|
+
}
|
|
550
|
+
return r.join('/');
|
|
551
|
+
};
|
|
552
|
+
/** KG4 (D3): base de uma resolucao relativa como o TypeScript a ve (a barra invertida tambem separa); `null` se nao e relativa ou sai da raiz. */
|
|
553
|
+
const baseDaSonda = (de, nome) => (/^\.\.?($|[\\/])/.test(nome) ? normalizar([...de.split('/').slice(0, -1), ...nome.replace(/\\/g, '/').split('/')]) : null);
|
|
554
|
+
/** `/// <reference path>`: relativo ao arquivo, com ou sem `./`. */
|
|
555
|
+
const baseDaReferencia = (de, nome) => (nome.startsWith('/') || nome.startsWith('\\') ? null : normalizar([...de.split('/').slice(0, -1), ...nome.replace(/\\/g, '/').split('/')]));
|
|
556
|
+
/**
|
|
557
|
+
* KG4 (D3): a resolucao de uma pasta le o `package.json` dela, e `main`, `types` e `typings` podem
|
|
558
|
+
* apontar para fora da base: os alvos entram como sondas, tambem com a barra invertida trocada, como o
|
|
559
|
+
* TypeScript le. `typesVersions` remapeia qualquer caminho, e entao a sonda casa tudo; o mesmo vale
|
|
560
|
+
* para `package.json` que nao e JSON estrito (CHECK, A1: o TypeScript aceita comentario e virgula
|
|
561
|
+
* final). Mudanca no proprio `package.json` extrai o TypeScript inteiro.
|
|
562
|
+
*/
|
|
563
|
+
const sondasDoPacote = (base, sondas) => {
|
|
564
|
+
for (const pasta of new Set([base, semExtensao(base)])) {
|
|
565
|
+
const arquivo = junta(pasta, 'package.json');
|
|
566
|
+
if (!arquivos.has(arquivo))
|
|
567
|
+
continue;
|
|
568
|
+
const v = lerJson(arquivo);
|
|
569
|
+
if (v === null || typeof v !== 'object' || Array.isArray(v)) {
|
|
570
|
+
sondas.add('');
|
|
571
|
+
continue;
|
|
572
|
+
}
|
|
573
|
+
const o = v;
|
|
574
|
+
if ('typesVersions' in o)
|
|
575
|
+
sondas.add('');
|
|
576
|
+
for (const campo of ['main', 'types', 'typings']) {
|
|
577
|
+
const alvo = o[campo];
|
|
578
|
+
if (typeof alvo !== 'string' || !alvo)
|
|
579
|
+
continue;
|
|
580
|
+
for (const forma of new Set([alvo, alvo.replace(/\\/g, '/')])) {
|
|
581
|
+
const t = forma.startsWith('/') ? null : caminhoLiteral(arquivo, `./${forma}`);
|
|
582
|
+
if (t !== null)
|
|
583
|
+
sondas.add(t);
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
};
|
|
588
|
+
const sondar = (base, sondas) => {
|
|
589
|
+
if (base === null)
|
|
590
|
+
return;
|
|
591
|
+
sondas.add(base);
|
|
592
|
+
sondasDoPacote(base, sondas);
|
|
593
|
+
};
|
|
490
594
|
// Primeira passada, em todos os arquivos: o alvo de cada especificador para o compilador e para o
|
|
491
595
|
// runtime. Onde divergem, a aresta de import vai ao que roda e nenhum simbolo passa por ali (D3).
|
|
492
596
|
const resolucoes = new Map();
|
|
493
597
|
const divergentesGlobais = new Set();
|
|
598
|
+
// KG4 (D3): por fonte, os caminhos a que as referencias de modulo resolvem e as bases das relativas.
|
|
599
|
+
const dependenciasDe = new Map(), sondasDe = new Map();
|
|
494
600
|
for (const fonte of e.fontes) {
|
|
495
601
|
const sf = programa.getSourceFile(absoluto(fonte.path));
|
|
496
602
|
if (!sf || sf.fileName !== absoluto(fonte.path))
|
|
497
603
|
continue;
|
|
498
604
|
const ext = extensao(fonte.path), mapa = new Map();
|
|
605
|
+
const dependencias = new Set(), sondas = new Set();
|
|
499
606
|
for (const chave of especificadoresEm(sf)) {
|
|
500
607
|
if (mapa.has(chave))
|
|
501
608
|
continue;
|
|
502
609
|
const esp = especificadorDaChave(chave), doCompilador = resolver(esp, fonte.path);
|
|
610
|
+
sondar(baseDaSonda(fonte.path, esp), sondas);
|
|
503
611
|
let alvo = doCompilador, divergente = false;
|
|
504
612
|
// Import so de tipo nao roda: vale o que o compilador liga. Fonte JavaScript roda no Node, e onde
|
|
505
613
|
// o Node falha (formato, escopo, alvo que nao carrega) nao ha aresta.
|
|
506
614
|
if (chave[0] !== 't' && EXTENSOES_JS.includes(ext)) {
|
|
507
615
|
const runtime = alvoNoNode(fonte.path, chave);
|
|
508
|
-
if (runtime !==
|
|
616
|
+
if (runtime.bruto !== null)
|
|
617
|
+
dependencias.add(runtime.bruto);
|
|
618
|
+
if (runtime.alvo !== doCompilador) {
|
|
509
619
|
divergente = true;
|
|
510
|
-
alvo = runtime;
|
|
620
|
+
alvo = runtime.alvo;
|
|
511
621
|
}
|
|
512
622
|
}
|
|
513
623
|
const impl = chave[0] !== 't' && !divergente && doCompilador !== null ? implementacaoDe(doCompilador) : null;
|
|
@@ -515,11 +625,45 @@ function extrairTypeScript(e, ts) {
|
|
|
515
625
|
divergente = true;
|
|
516
626
|
alvo = impl;
|
|
517
627
|
}
|
|
628
|
+
for (const x of [doCompilador, alvo])
|
|
629
|
+
if (x !== null)
|
|
630
|
+
dependencias.add(x);
|
|
518
631
|
mapa.set(chave, { alvo, divergente, doCompilador });
|
|
519
632
|
if (divergente)
|
|
520
633
|
divergentesGlobais.add(`${fonte.path}\u0000${chave}`);
|
|
521
634
|
}
|
|
635
|
+
// O que o TypeScript coleta e a varredura nao ve (tipo importado no JSDoc) tambem liga o checker a outro arquivo.
|
|
636
|
+
for (const nome of nomesDeModulo(sf)) {
|
|
637
|
+
sondar(baseDaSonda(fonte.path, nome), sondas);
|
|
638
|
+
const alvo = resolver(nome, fonte.path);
|
|
639
|
+
if (alvo !== null)
|
|
640
|
+
dependencias.add(alvo);
|
|
641
|
+
}
|
|
642
|
+
for (const r of sf.referencedFiles) {
|
|
643
|
+
const base = baseDaReferencia(fonte.path, r.fileName);
|
|
644
|
+
if (base === null)
|
|
645
|
+
continue;
|
|
646
|
+
sondas.add(base);
|
|
647
|
+
for (const x of [base, `${base}.ts`, `${base}.tsx`, `${base}.d.ts`, `${base}.js`, `${base}.jsx`])
|
|
648
|
+
if (arquivos.has(x))
|
|
649
|
+
dependencias.add(x);
|
|
650
|
+
}
|
|
522
651
|
resolucoes.set(fonte.path, mapa);
|
|
652
|
+
dependenciasDe.set(fonte.path, dependencias);
|
|
653
|
+
sondasDe.set(fonte.path, sondas);
|
|
654
|
+
}
|
|
655
|
+
// KG4 (D3): no programa parcial, todo alvo TS/JS das fontes precisa ser fonte, para ter formato e
|
|
656
|
+
// resolucoes; senao o caminho cresce e a extracao recomeca.
|
|
657
|
+
if (e.emitir) {
|
|
658
|
+
const noPrograma = new Set(e.fontes.map((f) => f.path)), faltantes = new Set();
|
|
659
|
+
for (const dependencias of dependenciasDe.values()) {
|
|
660
|
+
for (const d of dependencias) {
|
|
661
|
+
if (!noPrograma.has(d) && EXTENSOES_DO_PROGRAMA.includes(extensao(d)) && e.texto(d) !== undefined)
|
|
662
|
+
faltantes.add(d);
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
if (faltantes.size)
|
|
666
|
+
return { ...resultado, faltantes: [...faltantes].sort() };
|
|
523
667
|
}
|
|
524
668
|
// B-N2: modulo que importa, direto ou por outro modulo, um arquivo com import divergente pode reexportar
|
|
525
669
|
// o que o compilador liga e o runtime nao (`export *`, `module.exports = require(...)`). O fecho segue
|
|
@@ -577,6 +721,13 @@ function extrairTypeScript(e, ts) {
|
|
|
577
721
|
return false;
|
|
578
722
|
}
|
|
579
723
|
for (const fonte of e.fontes) {
|
|
724
|
+
if (e.emitir && !e.emitir.has(fonte.path))
|
|
725
|
+
continue;
|
|
726
|
+
// KG4 (D2): os achados ficam por arquivo, com as dependencias e as sondas, para as unidades do indice.
|
|
727
|
+
const saida = { nos: [], arestas: [], diagnosticos: [], lacunas: [] };
|
|
728
|
+
resultado.unidades.set(fonte.path, {
|
|
729
|
+
dependencias: [...(dependenciasDe.get(fonte.path) ?? [])].sort(), sondas: [...(sondasDe.get(fonte.path) ?? [])].sort(), achados: saida,
|
|
730
|
+
});
|
|
580
731
|
const sf = programa.getSourceFile(absoluto(fonte.path));
|
|
581
732
|
// Fonte que o compilador trocou por outra (pacote duplicado) nao e lida: o no seria do outro arquivo.
|
|
582
733
|
if (!sf || sf.fileName !== absoluto(fonte.path)) {
|
|
@@ -806,5 +957,24 @@ function extrairTypeScript(e, ts) {
|
|
|
806
957
|
for (const st of sf.statements)
|
|
807
958
|
visitar(st, null);
|
|
808
959
|
}
|
|
809
|
-
|
|
960
|
+
// KG4 (D3): quem declara no escopo global (visto de um modulo vazio, sem local que o esconda) ou
|
|
961
|
+
// aumenta modulo alcanca arquivo que nao o importa; mudanca nele reextrai o TypeScript inteiro.
|
|
962
|
+
const doEscopoGlobal = programa.getSourceFile(sintetico);
|
|
963
|
+
if (!doEscopoGlobal)
|
|
964
|
+
throw new Error('extracao.interna.escopo-global');
|
|
965
|
+
const globais = new Set();
|
|
966
|
+
for (const simbolo of checker.getSymbolsInScope(doEscopoGlobal, -1)) {
|
|
967
|
+
for (const d of simbolo.declarations ?? []) {
|
|
968
|
+
const p = relativo(d.getSourceFile().fileName);
|
|
969
|
+
if (p !== null)
|
|
970
|
+
globais.add(p);
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
for (const sf of programa.getSourceFiles()) {
|
|
974
|
+
const aumentos = sf.moduleAugmentations, p = relativo(sf.fileName);
|
|
975
|
+
if (p !== null && aumentos && aumentos.length)
|
|
976
|
+
globais.add(p);
|
|
977
|
+
}
|
|
978
|
+
resultado.globais = [...globais].sort();
|
|
979
|
+
return resultado;
|
|
810
980
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.textoAceito = exports.LACUNAS_SO_CONTAGEM = exports.EXTENSOES_MD = exports.EXTENSOES_TS = exports.CONFIG_SCHEMA = exports.RELATORIO_SCHEMA = exports.VERSAO_KG2 = exports.EXTRATOR_ARQUIVOS = exports.EXTRATOR_ID = exports.EXTRATOR_MD = exports.EXTRATOR_TS = void 0;
|
|
3
|
+
exports.textoAceito = exports.UNIDADES_SCHEMA = exports.LACUNAS_SO_CONTAGEM = exports.EXTENSOES_MD = exports.EXTENSOES_TS = exports.CONFIG_SCHEMA = exports.RELATORIO_SCHEMA = exports.VERSAO_KG2 = exports.EXTRATOR_ARQUIVOS = exports.EXTRATOR_ID = exports.EXTRATOR_MD = exports.EXTRATOR_TS = void 0;
|
|
4
4
|
exports.textoCurto = textoCurto;
|
|
5
5
|
exports.extensaoDe = extensaoDe;
|
|
6
6
|
exports.idDeBlob = idDeBlob;
|
|
@@ -34,6 +34,8 @@ exports.LACUNAS_SO_CONTAGEM = new Set([
|
|
|
34
34
|
'chamada-fora-de-simbolo', 'chamada-nao-resolvida', 'chamada-alvo-fora-do-grafo', 'chamada-por-tipo', 'import-sem-simbolo',
|
|
35
35
|
'id-sem-artefato', 'link-externo',
|
|
36
36
|
]);
|
|
37
|
+
/** RM-031 KG4 (D2): as unidades por arquivo que o indice guarda para a proxima revisao reaproveitar. */
|
|
38
|
+
exports.UNIDADES_SCHEMA = 'ork.graph-extraction-units/v0';
|
|
37
39
|
function falha(codigo, onde) {
|
|
38
40
|
throw new Error(onde ? `${codigo} em ${onde}` : codigo);
|
|
39
41
|
}
|
|
@@ -136,11 +138,144 @@ const zeros = '0'.repeat(64);
|
|
|
136
138
|
function contagemVazia(chaves) {
|
|
137
139
|
return Object.fromEntries(chaves.map((k) => [k, 0]));
|
|
138
140
|
}
|
|
141
|
+
/**
|
|
142
|
+
* A mudanca e por conteudo: o hash de cada caminho contra o da base, mais os novos e os removidos
|
|
143
|
+
* (renome e os dois). No TypeScript, semente e o arquivo mudado ou novo e o nao mudado cuja sonda casa
|
|
144
|
+
* caminho novo ou removido; afetado e a semente e quem alcanca semente ou caminho mudado pelas
|
|
145
|
+
* dependencias gravadas na base. `package.json` ou arquivo global na mudanca extrai o TypeScript inteiro.
|
|
146
|
+
*/
|
|
147
|
+
function planejarReuso(base, manifesto, fontesTs, fontesMd) {
|
|
148
|
+
const porCaminho = new Map(base.arquivos.map((u) => [u.path, u]));
|
|
149
|
+
const alterados = [], novos = [], removidos = [];
|
|
150
|
+
for (const [p, m] of manifesto) {
|
|
151
|
+
const u = porCaminho.get(p);
|
|
152
|
+
if (!u)
|
|
153
|
+
novos.push(p);
|
|
154
|
+
else if (u.source_hash !== m.source_hash)
|
|
155
|
+
alterados.push(p);
|
|
156
|
+
}
|
|
157
|
+
for (const p of porCaminho.keys())
|
|
158
|
+
if (!manifesto.has(p))
|
|
159
|
+
removidos.push(p);
|
|
160
|
+
for (const l of [alterados, novos, removidos])
|
|
161
|
+
l.sort(intelligence_graph_contract_1.compararUtf8);
|
|
162
|
+
const mudados = new Set([...alterados, ...novos, ...removidos]);
|
|
163
|
+
const md = new Map();
|
|
164
|
+
for (const f of fontesMd) {
|
|
165
|
+
const u = porCaminho.get(f.path);
|
|
166
|
+
if (u?.md && !mudados.has(f.path))
|
|
167
|
+
md.set(f.path, u.md);
|
|
168
|
+
}
|
|
169
|
+
const plano = { base, porCaminho, mudanca: { alterados, novos, removidos }, md, ts: { motivo: null, sementes: new Set(), afetados: new Set() } };
|
|
170
|
+
const ordenados = [...mudados].sort(intelligence_graph_contract_1.compararUtf8), globais = new Set(base.globais_ts), ts1 = new Set(fontesTs.map((f) => f.path));
|
|
171
|
+
const pacote = ordenados.find((p) => p === 'package.json' || p.endsWith('/package.json'));
|
|
172
|
+
const global = ordenados.find((p) => globais.has(p));
|
|
173
|
+
const semUnidade = fontesTs.find((f) => !mudados.has(f.path) && !porCaminho.get(f.path)?.ts);
|
|
174
|
+
if (pacote !== undefined)
|
|
175
|
+
plano.ts.motivo = `package.json na mudanca (${pacote})`;
|
|
176
|
+
else if (global !== undefined)
|
|
177
|
+
plano.ts.motivo = `arquivo global do TypeScript na mudanca (${global})`;
|
|
178
|
+
else if (semUnidade !== undefined)
|
|
179
|
+
plano.ts.motivo = `base sem a unidade TypeScript de ${semUnidade.path}`;
|
|
180
|
+
if (plano.ts.motivo !== null)
|
|
181
|
+
return plano;
|
|
182
|
+
const existencia = [...novos, ...removidos];
|
|
183
|
+
for (const p of ts1) {
|
|
184
|
+
if (mudados.has(p))
|
|
185
|
+
plano.ts.sementes.add(p);
|
|
186
|
+
else if (existencia.length && (porCaminho.get(p)?.ts).sondas.some((b) => existencia.some((q) => (0, intelligence_graph_extract_ts_1.casaSonda)(b, q))))
|
|
187
|
+
plano.ts.sementes.add(p);
|
|
188
|
+
}
|
|
189
|
+
const dependentes = new Map();
|
|
190
|
+
for (const u of base.arquivos) {
|
|
191
|
+
for (const d of u.ts?.dependencias ?? []) {
|
|
192
|
+
const lista = dependentes.get(d);
|
|
193
|
+
if (lista)
|
|
194
|
+
lista.push(u.path);
|
|
195
|
+
else
|
|
196
|
+
dependentes.set(d, [u.path]);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
const vistos = new Set(), fila = [...plano.ts.sementes, ...mudados];
|
|
200
|
+
while (fila.length) {
|
|
201
|
+
const q = fila.pop();
|
|
202
|
+
if (vistos.has(q))
|
|
203
|
+
continue;
|
|
204
|
+
vistos.add(q);
|
|
205
|
+
for (const x of dependentes.get(q) ?? [])
|
|
206
|
+
if (!vistos.has(x))
|
|
207
|
+
fila.push(x);
|
|
208
|
+
}
|
|
209
|
+
// CHECK (B1): quem usa um global nao importa o arquivo dele, e o global depende do que esse arquivo
|
|
210
|
+
// importa (heranca, tipo importado, `export *` atras de UMD, aumento que estende tipo de outro modulo).
|
|
211
|
+
const globalAlcancado = [...globais].sort(intelligence_graph_contract_1.compararUtf8).find((g) => vistos.has(g));
|
|
212
|
+
if (globalAlcancado !== undefined) {
|
|
213
|
+
plano.ts.motivo = `dependencia de arquivo global na mudanca (${globalAlcancado})`;
|
|
214
|
+
return plano;
|
|
215
|
+
}
|
|
216
|
+
for (const p of vistos)
|
|
217
|
+
if (ts1.has(p))
|
|
218
|
+
plano.ts.afetados.add(p);
|
|
219
|
+
return plano;
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* KG4 (D3): extrai os afetados num programa parcial: eles, os globais e o fecho direto pelas
|
|
223
|
+
* dependencias da base (a semente descobre as suas no programa, que cresce ate fechar). O resto vem
|
|
224
|
+
* da base. Afetado que passa a declarar global devolve o motivo, e o TypeScript sai inteiro.
|
|
225
|
+
*/
|
|
226
|
+
function extrairTsParcial(plano, entrada, extrair) {
|
|
227
|
+
const { afetados, sementes } = plano.ts, ts1 = new Set(entrada.fontes.map((f) => f.path));
|
|
228
|
+
const daBase = (p) => plano.porCaminho.get(p)?.ts;
|
|
229
|
+
const unidades = new Map(), globais = [...plano.base.globais_ts];
|
|
230
|
+
const raizes = new Set();
|
|
231
|
+
const incluir = (inicio) => {
|
|
232
|
+
const fila = [...inicio];
|
|
233
|
+
while (fila.length) {
|
|
234
|
+
const p = fila.pop();
|
|
235
|
+
if (raizes.has(p) || !ts1.has(p))
|
|
236
|
+
continue;
|
|
237
|
+
raizes.add(p);
|
|
238
|
+
if (!sementes.has(p))
|
|
239
|
+
for (const d of daBase(p).dependencias)
|
|
240
|
+
if (!raizes.has(d))
|
|
241
|
+
fila.push(d);
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
let r = null;
|
|
245
|
+
if (afetados.size) {
|
|
246
|
+
incluir([...afetados, ...globais]);
|
|
247
|
+
for (;;) {
|
|
248
|
+
r = extrair({ ...entrada, fontes: entrada.fontes.filter((f) => raizes.has(f.path)), emitir: afetados });
|
|
249
|
+
if (!r.faltantes.length)
|
|
250
|
+
break;
|
|
251
|
+
const antes = raizes.size;
|
|
252
|
+
incluir(r.faltantes);
|
|
253
|
+
if (raizes.size === antes)
|
|
254
|
+
falha('extracao.interna.programa-parcial');
|
|
255
|
+
}
|
|
256
|
+
const daBaseGlobal = new Set(globais), doPrograma = new Set(r.globais);
|
|
257
|
+
const novo = r.globais.find((g) => !daBaseGlobal.has(g));
|
|
258
|
+
if (novo !== undefined)
|
|
259
|
+
return { motivo: `arquivo passa a declarar global no TypeScript (${novo})`, unidades, globais, programa: raizes.size };
|
|
260
|
+
const sumiu = globais.find((g) => ts1.has(g) && !doPrograma.has(g));
|
|
261
|
+
if (sumiu !== undefined)
|
|
262
|
+
return { motivo: `global da base fora do escopo global (${sumiu})`, unidades, globais, programa: raizes.size };
|
|
263
|
+
}
|
|
264
|
+
for (const f of entrada.fontes) {
|
|
265
|
+
const u = afetados.has(f.path) ? r?.unidades.get(f.path) : daBase(f.path);
|
|
266
|
+
if (!u)
|
|
267
|
+
falha('extracao.interna.unidade-ts-ausente');
|
|
268
|
+
unidades.set(f.path, u);
|
|
269
|
+
}
|
|
270
|
+
return { motivo: null, unidades, globais, programa: raizes.size };
|
|
271
|
+
}
|
|
139
272
|
/**
|
|
140
273
|
* Extrai o grafo das fontes fornecidas. Lanca `extracao.*` para entrada invalida ou teto do
|
|
141
274
|
* contrato estourado (D10), e `grafo.*` se o resultado violar o contrato (defeito do extrator).
|
|
275
|
+
* RM-031 KG4: com `base` (as unidades do indice de outra revisao, do mesmo extrator), reextrai so o
|
|
276
|
+
* que a mudanca alcanca; o resultado e o mesmo, byte a byte, da extracao sem base.
|
|
142
277
|
*/
|
|
143
|
-
function extrairGrafo(entrada, parser) {
|
|
278
|
+
function extrairGrafo(entrada, parser, base = null) {
|
|
144
279
|
const { ts, unicode, markdown, javascript } = parser;
|
|
145
280
|
if (!/^[0-9]+(\.[0-9]+)*$/.test(unicode))
|
|
146
281
|
falha('extracao.entrada.unicode-invalido');
|
|
@@ -224,18 +359,40 @@ function extrairGrafo(entrada, parser) {
|
|
|
224
359
|
// B1: a raiz virtual do compilador deriva do manifesto; um caminho do repositorio nao a nomeia.
|
|
225
360
|
const raiz = `/ork-${(0, intelligence_graph_contract_1.sha256DoCanonico)([...manifesto.values()].map((m) => [m.path, m.source_hash])).slice(0, 32)}`;
|
|
226
361
|
const aceitaFragmento = (f) => f.length >= 1 && f.length <= intelligence_graph_contract_1.GRAFO_LIMITES.fragmento && (0, exports.textoAceito)(f);
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
362
|
+
const entradaTs = { fontes: fontesTs, arquivos: caminhos, texto, raiz, aceitaFragmento, sintaxe: javascript.sintaxe, extrator: exports.EXTRATOR_TS };
|
|
363
|
+
const extrairTs = (e) => semEstouro(exports.EXTRATOR_TS, () => (0, intelligence_graph_extract_ts_1.extrairTypeScript)(e, ts));
|
|
364
|
+
// KG4 (D1, D3, D4): com as unidades de uma base, so o que a mudanca alcanca e extraido de novo.
|
|
365
|
+
const plano = base ? planejarReuso(base, manifesto, fontesTs, fontesMd) : null;
|
|
366
|
+
let tsDaBase = null;
|
|
367
|
+
if (plano && plano.ts.motivo === null)
|
|
368
|
+
tsDaBase = extrairTsParcial(plano, entradaTs, extrairTs);
|
|
369
|
+
const motivoTs = plano ? plano.ts.motivo ?? tsDaBase?.motivo ?? null : null;
|
|
370
|
+
let unidadesTs, globaisTs;
|
|
371
|
+
if (tsDaBase && tsDaBase.motivo === null) {
|
|
372
|
+
unidadesTs = tsDaBase.unidades;
|
|
373
|
+
globaisTs = tsDaBase.globais;
|
|
374
|
+
}
|
|
375
|
+
else {
|
|
376
|
+
const r = extrairTs(entradaTs);
|
|
377
|
+
unidadesTs = r.unidades;
|
|
378
|
+
globaisTs = r.globais;
|
|
379
|
+
}
|
|
380
|
+
for (const f of fontesTs) {
|
|
381
|
+
const u = unidadesTs.get(f.path);
|
|
382
|
+
if (!u)
|
|
383
|
+
falha('extracao.interna.unidade-ts-ausente');
|
|
384
|
+
juntar(u.achados);
|
|
385
|
+
}
|
|
230
386
|
// B4: chave `caminho#fragmento` como o frontmatter a escreve; duas refs na mesma chave ficam ambiguas.
|
|
231
387
|
const simbolos = new Map();
|
|
232
388
|
for (const r of achados.nos.filter((x) => x.kind === 'symbol')) {
|
|
233
389
|
const chave = `${r.path}#${r.fragment}`, antes = simbolos.get(chave);
|
|
234
390
|
simbolos.set(chave, antes === undefined || (antes && antes.path === r.path && antes.fragment === r.fragment) ? r : null);
|
|
235
391
|
}
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
392
|
+
const estruturas = semEstouro(exports.EXTRATOR_MD, () => fontesMd.map((f) => plano?.md.get(f.path)
|
|
393
|
+
?? (0, intelligence_graph_extract_md_1.estruturarMarkdown)(f, { aceitaFragmento, analisar: markdown.analisar, referencia: markdown.referencia })));
|
|
394
|
+
juntar(semEstouro(exports.EXTRATOR_MD, () => (0, intelligence_graph_extract_md_1.ligarMarkdown)({
|
|
395
|
+
estruturas, codigo: fontesTs, arquivos: caminhos, simbolos, aceitaFragmento, extratorMd: exports.EXTRATOR_MD, extratorId: exports.EXTRATOR_ID,
|
|
239
396
|
})));
|
|
240
397
|
const extratores = [
|
|
241
398
|
{ extractor_id: exports.EXTRATOR_ARQUIVOS, extractor_version: exports.VERSAO_KG2 },
|
|
@@ -365,5 +522,25 @@ function extrairGrafo(entrada, parser) {
|
|
|
365
522
|
lacunas_por_categoria: Object.fromEntries([...porCategoria.entries()].sort((a, b) => (0, intelligence_graph_contract_1.compararUtf8)(a[0], b[0]))),
|
|
366
523
|
lacunas: [...listadas.entries()].sort((a, b) => (0, intelligence_graph_contract_1.compararUtf8)(a[0], b[0])).map(([, l]) => l),
|
|
367
524
|
};
|
|
368
|
-
|
|
525
|
+
const estruturaDe = new Map(estruturas.map((x) => [x.path, x]));
|
|
526
|
+
const unidades = {
|
|
527
|
+
schema: exports.UNIDADES_SCHEMA, snapshot_id: grafo.snapshot.snapshot_id, graph_digest: digest, globais_ts: globaisTs,
|
|
528
|
+
arquivos: grafo.snapshot.source_manifest.map((m) => ({
|
|
529
|
+
path: m.path, source_hash: m.source_hash, ts: unidadesTs.get(m.path) ?? null, md: estruturaDe.get(m.path) ?? null,
|
|
530
|
+
})),
|
|
531
|
+
};
|
|
532
|
+
let reaproveitamento = null;
|
|
533
|
+
if (plano) {
|
|
534
|
+
const parcial = motivoTs === null, afetados = [...plano.ts.afetados].sort(intelligence_graph_contract_1.compararUtf8);
|
|
535
|
+
reaproveitamento = {
|
|
536
|
+
mudanca: { alterados: plano.mudanca.alterados.length, novos: plano.mudanca.novos.length, removidos: plano.mudanca.removidos.length },
|
|
537
|
+
ts: {
|
|
538
|
+
modo: !parcial ? 'inteiro' : afetados.length ? 'parcial' : 'reaproveitado', motivo: motivoTs,
|
|
539
|
+
reextraidos: parcial ? afetados : fontesTs.map((f) => f.path), programa: parcial ? tsDaBase?.programa ?? 0 : fontesTs.length,
|
|
540
|
+
reaproveitados: parcial ? fontesTs.length - afetados.length : 0,
|
|
541
|
+
},
|
|
542
|
+
md: { reextraidos: fontesMd.filter((f) => !plano.md.has(f.path)).map((f) => f.path), reaproveitados: plano.md.size },
|
|
543
|
+
};
|
|
544
|
+
}
|
|
545
|
+
return { grafo, digest, relatorio, unidades, reaproveitamento };
|
|
369
546
|
}
|