@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.
Files changed (63) hide show
  1. package/adapters/claude-code/README.md +11 -2
  2. package/adapters/claude-code/commands/onboarding.md +3 -2
  3. package/adapters/openclaw/README.md +11 -0
  4. package/adapters/openclaw/dist/index.js +1 -1
  5. package/adapters/openclaw/openclaw.plugin.json +1 -1
  6. package/adapters/openclaw/package.json +1 -1
  7. package/adapters/openclaw/src/index.ts +1 -1
  8. package/dist/adapters/claude-bg.js +20 -6
  9. package/dist/auditrun.js +1 -1
  10. package/dist/claim-lint.js +3 -1
  11. package/dist/docs.js +71 -3
  12. package/dist/doctor.js +209 -32
  13. package/dist/entrega-pr.js +7 -0
  14. package/dist/fabrica-estado.js +21 -3
  15. package/dist/fix.js +3 -3
  16. package/dist/hitl-resumo.js +24 -1
  17. package/dist/hosts.js +7 -3
  18. package/dist/index.js +42 -14
  19. package/dist/init.js +11 -5
  20. package/dist/integracoes-locais.js +2 -0
  21. package/dist/intelligence-graph-cli.js +181 -27
  22. package/dist/intelligence-graph-contract.js +71 -12
  23. package/dist/intelligence-graph-extract-md.js +41 -23
  24. package/dist/intelligence-graph-extract-ts.js +181 -11
  25. package/dist/intelligence-graph-extract.js +186 -9
  26. package/dist/intelligence-graph-index.js +205 -24
  27. package/dist/intelligence-graph-repo.js +10 -0
  28. package/dist/leases.js +16 -0
  29. package/dist/ledger.js +9 -0
  30. package/dist/licoes.js +5 -4
  31. package/dist/liveness.js +2 -1
  32. package/dist/maestro-runtime.js +3 -0
  33. package/dist/manifest.js +24 -0
  34. package/dist/mcp-artifacts.js +2 -0
  35. package/dist/mcp-git.js +60 -12
  36. package/dist/mcp-grafo-worker.js +91 -0
  37. package/dist/mcp-grafo.js +233 -0
  38. package/dist/mcp-install.js +2 -1
  39. package/dist/mcp-server.js +9 -0
  40. package/dist/mcp-ship.js +2 -0
  41. package/dist/mcp-verify.js +3 -1
  42. package/dist/network-roadmap.js +5 -5
  43. package/dist/ocupacao.js +6 -2
  44. package/dist/onboarding.js +9 -4
  45. package/dist/orquestracao.js +2 -1
  46. package/dist/parado-no-condutor.js +933 -0
  47. package/dist/policies.js +27 -2
  48. package/dist/prova-ativacao.js +345 -0
  49. package/dist/pulse-delivery.js +27 -6
  50. package/dist/pulse.js +53 -3
  51. package/dist/roadmap-status.js +142 -7
  52. package/dist/runtime-context.js +5 -1
  53. package/dist/sessoes-inventario.js +7 -1
  54. package/dist/sessoes.js +4 -1
  55. package/dist/ship.js +25 -1
  56. package/dist/thread.js +21 -2
  57. package/dist/util.js +71 -4
  58. package/dist/verify.js +2 -2
  59. package/dist/worktree.js +29 -6
  60. package/eval/casos/onboarding.json +1 -1
  61. package/package.json +1 -1
  62. package/skills/core/onboarding/SKILL.md +3 -2
  63. 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
- function criarHost(ts, e) {
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 saida = { nos: [], arestas: [], diagnosticos: [], lacunas: [] };
145
+ const resultado = { unidades: new Map(), globais: [], faltantes: [] };
113
146
  if (e.fontes.length === 0)
114
- return saida;
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 host = criarHost(ts, e), opts = opcoes(ts);
118
- const programa = ts.createProgram(e.fontes.map((f) => absoluto(f.path)), opts, host);
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 !== doCompilador) {
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
- return saida;
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
- juntar(semEstouro(exports.EXTRATOR_TS, () => (0, intelligence_graph_extract_ts_1.extrairTypeScript)({
228
- fontes: fontesTs, arquivos: caminhos, texto, raiz, aceitaFragmento, sintaxe: javascript.sintaxe, extrator: exports.EXTRATOR_TS,
229
- }, ts)));
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
- juntar(semEstouro(exports.EXTRATOR_MD, () => (0, intelligence_graph_extract_md_1.extrairMarkdown)({
237
- fontes: fontesMd, codigo: fontesTs, arquivos: caminhos, simbolos, aceitaFragmento, analisar: markdown.analisar,
238
- referencia: markdown.referencia, extratorMd: exports.EXTRATOR_MD, extratorId: exports.EXTRATOR_ID,
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
- return { grafo, digest, relatorio };
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
  }