@orkastery/cli 0.5.1 → 0.5.6

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 (260) hide show
  1. package/adapters/claude-code/README.md +158 -0
  2. package/adapters/claude-code/agents/ork-master.md +2 -0
  3. package/adapters/claude-code/commands/master.md +8 -0
  4. package/adapters/claude-code/commands/ork.md +9 -0
  5. package/adapters/claude-code/mods/autoridade.json +56 -0
  6. package/adapters/claude-code/mods/ork-clima/.claude-plugin/plugin.json +14 -0
  7. package/adapters/claude-code/mods/ork-clima/hooks/contas.ts +283 -0
  8. package/adapters/claude-code/mods/ork-clima/hooks/hooks.json +3 -0
  9. package/adapters/claude-code/mods/ork-clima/hooks/previsao.ts +393 -0
  10. package/adapters/claude-code/mods/ork-clima/hooks/register.ts +205 -0
  11. package/adapters/claude-code/mods/ork-clima/orkastery-mod.json +5 -0
  12. package/adapters/claude-code/mods/ork-clima/types/index.d.ts +80 -0
  13. package/adapters/claude-code/mods/ork-freio/.claude-plugin/plugin.json +14 -0
  14. package/adapters/claude-code/mods/ork-freio/hooks/carga.ts +160 -0
  15. package/adapters/claude-code/mods/ork-freio/hooks/conferencia.ts +309 -0
  16. package/adapters/claude-code/mods/ork-freio/hooks/hooks.json +3 -0
  17. package/adapters/claude-code/mods/ork-freio/hooks/lote.ts +270 -0
  18. package/adapters/claude-code/mods/ork-freio/hooks/register.ts +285 -0
  19. package/adapters/claude-code/mods/ork-freio/orkastery-mod.json +5 -0
  20. package/adapters/claude-code/mods/ork-freio/types/index.d.ts +127 -0
  21. package/adapters/claude-code/mods/ork-mesa/.claude-plugin/plugin.json +14 -0
  22. package/adapters/claude-code/mods/ork-mesa/hooks/cartoes.tsx +246 -0
  23. package/adapters/claude-code/mods/ork-mesa/hooks/decidir.ts +110 -0
  24. package/adapters/claude-code/mods/ork-mesa/hooks/fila.ts +253 -0
  25. package/adapters/claude-code/mods/ork-mesa/hooks/hooks.json +3 -0
  26. package/adapters/claude-code/mods/ork-mesa/hooks/obvia.ts +98 -0
  27. package/adapters/claude-code/mods/ork-mesa/hooks/register.tsx +493 -0
  28. package/adapters/claude-code/mods/ork-mesa/orkastery-mod.json +5 -0
  29. package/adapters/claude-code/mods/ork-mesa/types/index.d.ts +131 -0
  30. package/adapters/claude-code/mods/ork-pista/.claude-plugin/plugin.json +14 -0
  31. package/adapters/claude-code/mods/ork-pista/hooks/acoes.ts +71 -0
  32. package/adapters/claude-code/mods/ork-pista/hooks/ci.ts +117 -0
  33. package/adapters/claude-code/mods/ork-pista/hooks/hooks.json +3 -0
  34. package/adapters/claude-code/mods/ork-pista/hooks/pista.tsx +356 -0
  35. package/adapters/claude-code/mods/ork-pista/hooks/register.tsx +439 -0
  36. package/adapters/claude-code/mods/ork-pista/orkastery-mod.json +5 -0
  37. package/adapters/claude-code/mods/ork-pista/types/index.d.ts +85 -0
  38. package/adapters/claude-code/mods/ork-ponte/.claude-plugin/plugin.json +13 -0
  39. package/adapters/claude-code/mods/ork-ponte/hooks/faixa.tsx +57 -0
  40. package/adapters/claude-code/mods/ork-ponte/hooks/guarda.ts +410 -0
  41. package/adapters/claude-code/mods/ork-ponte/hooks/hooks.json +3 -0
  42. package/adapters/claude-code/mods/ork-ponte/hooks/mostrado.ts +55 -0
  43. package/adapters/claude-code/mods/ork-ponte/hooks/nucleo.ts +157 -0
  44. package/adapters/claude-code/mods/ork-ponte/hooks/papel.ts +42 -0
  45. package/adapters/claude-code/mods/ork-ponte/hooks/register.ts +331 -0
  46. package/adapters/claude-code/mods/ork-ponte/hooks/vigia.ts +76 -0
  47. package/adapters/claude-code/mods/ork-ponte/orkastery-mod.json +5 -0
  48. package/adapters/claude-code/mods/ork-ponte/types/index.d.ts +178 -0
  49. package/adapters/claude-code/mods/ork-portao/.claude-plugin/plugin.json +14 -0
  50. package/adapters/claude-code/mods/ork-portao/hooks/hooks.json +3 -0
  51. package/adapters/claude-code/mods/ork-portao/hooks/lease.ts +201 -0
  52. package/adapters/claude-code/mods/ork-portao/hooks/plano.ts +304 -0
  53. package/adapters/claude-code/mods/ork-portao/hooks/register.ts +339 -0
  54. package/adapters/claude-code/mods/ork-portao/hooks/verify.ts +152 -0
  55. package/adapters/claude-code/mods/ork-portao/orkastery-mod.json +5 -0
  56. package/adapters/claude-code/mods/ork-portao/types/index.d.ts +60 -0
  57. package/adapters/claude-code/mods/ork-radar/.claude-plugin/plugin.json +14 -0
  58. package/adapters/claude-code/mods/ork-radar/hooks/colisao.ts +420 -0
  59. package/adapters/claude-code/mods/ork-radar/hooks/hooks.json +3 -0
  60. package/adapters/claude-code/mods/ork-radar/hooks/mapa.tsx +322 -0
  61. package/adapters/claude-code/mods/ork-radar/hooks/register.tsx +470 -0
  62. package/adapters/claude-code/mods/ork-radar/orkastery-mod.json +5 -0
  63. package/adapters/claude-code/mods/ork-radar/types/index.d.ts +72 -0
  64. package/adapters/claude-code/mods/ork-sentinela/.claude-plugin/plugin.json +14 -0
  65. package/adapters/claude-code/mods/ork-sentinela/hooks/alertas.ts +85 -0
  66. package/adapters/claude-code/mods/ork-sentinela/hooks/despacho.ts +156 -0
  67. package/adapters/claude-code/mods/ork-sentinela/hooks/hooks.json +3 -0
  68. package/adapters/claude-code/mods/ork-sentinela/hooks/register.ts +511 -0
  69. package/adapters/claude-code/mods/ork-sentinela/hooks/vigia.ts +186 -0
  70. package/adapters/claude-code/mods/ork-sentinela/orkastery-mod.json +5 -0
  71. package/adapters/claude-code/mods/ork-sentinela/types/index.d.ts +65 -0
  72. package/adapters/claude-code/mods/ork-torre/.claude-plugin/plugin.json +14 -0
  73. package/adapters/claude-code/mods/ork-torre/hooks/agenda.ts +60 -0
  74. package/adapters/claude-code/mods/ork-torre/hooks/detalhe.tsx +236 -0
  75. package/adapters/claude-code/mods/ork-torre/hooks/frases.ts +42 -0
  76. package/adapters/claude-code/mods/ork-torre/hooks/hooks.json +3 -0
  77. package/adapters/claude-code/mods/ork-torre/hooks/painel.tsx +344 -0
  78. package/adapters/claude-code/mods/ork-torre/hooks/rede.ts +97 -0
  79. package/adapters/claude-code/mods/ork-torre/hooks/register.tsx +437 -0
  80. package/adapters/claude-code/mods/ork-torre/orkastery-mod.json +5 -0
  81. package/adapters/claude-code/mods/ork-torre/types/index.d.ts +108 -0
  82. package/adapters/codex/skills/ork/SKILL.md +18 -3
  83. package/adapters/hermes/README.md +1 -0
  84. package/adapters/hermes/bin/ork-network-status.sh +10 -0
  85. package/adapters/hermes/hermes.plugin.json +1 -0
  86. package/adapters/hermes/hitl-ingress/__init__.py +2 -2
  87. package/adapters/hermes/skills/orkastery-devmaster/SKILL.md +7 -6
  88. package/adapters/openclaw/README.md +26 -5
  89. package/adapters/openclaw/dist/hitl-ingress.js +2 -2
  90. package/adapters/openclaw/dist/index.js +46 -11
  91. package/adapters/openclaw/openclaw.plugin.json +5 -1
  92. package/adapters/openclaw/package.json +2 -2
  93. package/adapters/openclaw/src/hitl-ingress.ts +2 -2
  94. package/adapters/openclaw/src/index.ts +49 -11
  95. package/assets/docs-en/markdownlint-cli2.jsonc +22 -0
  96. package/assets/docs-en/padroes/documentacao-de-produto.md +135 -0
  97. package/assets/docs-en/padroes/roadmap-de-produto.md +100 -0
  98. package/assets/docs-en/produto/README.md +14 -0
  99. package/assets/docs-en/produto/_modelo-feature.md +62 -0
  100. package/assets/docs-en/roadmap/README.md +14 -0
  101. package/assets/docs-en/roadmap/_modelo-item.md +75 -0
  102. package/assets/orkmind_bridge.py +117 -9
  103. package/dist/adapters/claude-bg.js +116 -18
  104. package/dist/adapters/codex.js +14 -4
  105. package/dist/agents-md.js +24 -14
  106. package/dist/auditrun.js +22 -3
  107. package/dist/board.js +20 -17
  108. package/dist/branch-de-estado.js +88 -10
  109. package/dist/busca-semantica.js +52 -12
  110. package/dist/canarios-rm057.js +149 -0
  111. package/dist/canarios.js +2 -0
  112. package/dist/catalogo.js +33 -0
  113. package/dist/ci.js +21 -8
  114. package/dist/claims-conferir.js +342 -0
  115. package/dist/claims.js +41 -1
  116. package/dist/company-brain-client.js +36 -1
  117. package/dist/company-brain-context.js +158 -22
  118. package/dist/company-brain-journal.js +2 -1
  119. package/dist/company-brain-migration.js +36 -1
  120. package/dist/conducao-sentinela.js +388 -0
  121. package/dist/conducao-texto.js +12 -18
  122. package/dist/conducao.js +122 -30
  123. package/dist/conhecimento.js +490 -0
  124. package/dist/docs.js +70 -17
  125. package/dist/doctor-pulse-cron.js +349 -0
  126. package/dist/doctor-rede.js +150 -0
  127. package/dist/doctor.js +195 -111
  128. package/dist/entrega-pr.js +42 -29
  129. package/dist/escopo-escrita.js +10 -1
  130. package/dist/esgotamentos.js +181 -0
  131. package/dist/estado-thread.js +23 -1
  132. package/dist/experiencia-instalacao.js +45 -25
  133. package/dist/experiencia.js +12 -2
  134. package/dist/fabrica-batida.js +310 -0
  135. package/dist/fabrica-estado.js +52 -14
  136. package/dist/fabrica-publicar.js +9 -1
  137. package/dist/fechamento.js +20 -66
  138. package/dist/fila-pesada.js +444 -0
  139. package/dist/fix.js +3 -2
  140. package/dist/forja.js +31 -7
  141. package/dist/gates.js +3 -24
  142. package/dist/handoff.js +10 -6
  143. package/dist/hitl-contract.js +59 -4
  144. package/dist/hitl-curto.js +11 -6
  145. package/dist/hitl-gates.js +5 -0
  146. package/dist/hitl-local-receipt.js +2 -0
  147. package/dist/hitl-lote.js +5 -2
  148. package/dist/hitl-resumo.js +38 -3
  149. package/dist/hitl-tempo-parado.js +201 -0
  150. package/dist/hitl-texto-livre.js +5 -5
  151. package/dist/hitl.js +29 -33
  152. package/dist/horario.js +41 -7
  153. package/dist/hosts.js +104 -11
  154. package/dist/impedimento.js +299 -0
  155. package/dist/index.js +961 -542
  156. package/dist/indice-vetorial.js +87 -10
  157. package/dist/indice.js +9 -1
  158. package/dist/init.js +56 -43
  159. package/dist/instabilidade.js +144 -0
  160. package/dist/integracoes-locais.js +4 -0
  161. package/dist/intelligence-graph-cli.js +170 -16
  162. package/dist/intelligence-graph-contexto.js +254 -0
  163. package/dist/intelligence-graph-contract.js +2 -1
  164. package/dist/intelligence-graph-extract-md.js +56 -4
  165. package/dist/intelligence-graph-extract-ts.js +71 -3
  166. package/dist/intelligence-graph-parsers.js +6 -4
  167. package/dist/intelligence-graph-query.js +66 -19
  168. package/dist/leases.js +664 -87
  169. package/dist/ledger-stats.js +15 -1
  170. package/dist/ledger.js +18 -1
  171. package/dist/licoes.js +1 -0
  172. package/dist/liveness.js +6 -3
  173. package/dist/locale-ajuda.js +761 -0
  174. package/dist/locale-gerados.js +160 -0
  175. package/dist/locale-master.js +99 -0
  176. package/dist/locale-onboarding.js +92 -0
  177. package/dist/locale-phase.js +151 -0
  178. package/dist/locale-pulse.js +211 -0
  179. package/dist/locale-retry.js +385 -0
  180. package/dist/locale-roadmap.js +102 -0
  181. package/dist/locale-ship.js +494 -0
  182. package/dist/locale-thread.js +277 -0
  183. package/dist/locale-verify.js +125 -0
  184. package/dist/locale.js +714 -0
  185. package/dist/maestro-runtime.js +4 -1
  186. package/dist/manifest.js +149 -4
  187. package/dist/maquina.js +23 -4
  188. package/dist/master-digest.js +6 -1
  189. package/dist/master-nota.js +18 -11
  190. package/dist/master.js +145 -31
  191. package/dist/mcp-artifacts.js +43 -3
  192. package/dist/mcp-git.js +42 -8
  193. package/dist/mcp-grafo-worker.js +5 -3
  194. package/dist/mcp-grafo.js +55 -6
  195. package/dist/mcp-server.js +22 -3
  196. package/dist/mcp-ship.js +3 -1
  197. package/dist/mcp-verify.js +6 -1
  198. package/dist/memoria.js +48 -6
  199. package/dist/modos.js +3 -6
  200. package/dist/network-roadmap.js +196 -18
  201. package/dist/noctum-arco.js +911 -0
  202. package/dist/noctum-capacidade.js +141 -0
  203. package/dist/noctum-envelope.js +810 -0
  204. package/dist/noctum-execucao.js +304 -0
  205. package/dist/noctum-fila.js +280 -0
  206. package/dist/ocupacao.js +40 -5
  207. package/dist/onboarding.js +40 -21
  208. package/dist/orkmind.js +99 -7
  209. package/dist/orquestracao.js +24 -21
  210. package/dist/parado-no-condutor.js +219 -38
  211. package/dist/phase.js +160 -52
  212. package/dist/policies.js +56 -5
  213. package/dist/portfolio.js +8 -3
  214. package/dist/postura-local.js +234 -0
  215. package/dist/preflight.js +11 -10
  216. package/dist/procedencia.js +225 -0
  217. package/dist/projeto-alvo.js +132 -5
  218. package/dist/prompts.js +40 -1
  219. package/dist/prova-ativacao.js +75 -6
  220. package/dist/pulse-delivery.js +47 -4
  221. package/dist/pulse.js +98 -19
  222. package/dist/ratelimit.js +24 -6
  223. package/dist/recall.js +3 -1
  224. package/dist/redacao-saida.js +63 -0
  225. package/dist/rede-adesao.js +399 -0
  226. package/dist/rede-forja.js +289 -0
  227. package/dist/rede-projetos.js +239 -0
  228. package/dist/rede-status.js +337 -0
  229. package/dist/rede.js +980 -0
  230. package/dist/retry.js +291 -140
  231. package/dist/roadmap-reservas.js +20 -3
  232. package/dist/roadmap-status.js +150 -34
  233. package/dist/runtime-context.js +181 -3
  234. package/dist/runtime-profiles.js +22 -1
  235. package/dist/runtimes.js +2 -1
  236. package/dist/saida-segura.js +58 -0
  237. package/dist/session-events.js +7 -3
  238. package/dist/session-watcher-claude.js +120 -24
  239. package/dist/session-watcher.js +185 -33
  240. package/dist/sessoes-contas.js +187 -0
  241. package/dist/sessoes-inventario.js +6 -18
  242. package/dist/sessoes.js +83 -2
  243. package/dist/ship-publicar.js +800 -0
  244. package/dist/ship.js +157 -88
  245. package/dist/steal.js +77 -0
  246. package/dist/thread-close.js +3 -2
  247. package/dist/thread.js +299 -49
  248. package/dist/util.js +8 -1
  249. package/dist/verify.js +141 -52
  250. package/dist/vida-da-thread.js +254 -0
  251. package/dist/worktree-local.js +165 -0
  252. package/dist/worktree.js +25 -2
  253. package/dist/yaml.js +27 -8
  254. package/eval/fixtures/fx-pedido-colado/caso.json +20 -0
  255. package/package.json +7 -3
  256. package/references/definition-of-done.md +1 -1
  257. package/schemas/code-artifact-graph.v1.schema.json +2 -1
  258. package/skills/README.md +1 -1
  259. package/skills/core/orkastery-bootstrap/SKILL.md +2 -2
  260. package/skills/phases/master-metrics/SKILL.md +8 -3
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.TETO_DE_LINHAS_DE_TABELA = exports.TOKENS_SEM_MENCAO = exports.CHAVES_DO_FRONTMATTER = exports.PADRAO_DE_ID = void 0;
4
4
  exports.slugDeTexto = slugDeTexto;
5
+ exports.caminhoDaCitacao = caminhoDaCitacao;
5
6
  exports.estruturarMarkdown = estruturarMarkdown;
6
7
  exports.extrairMarkdown = extrairMarkdown;
7
8
  exports.ligarMarkdown = ligarMarkdown;
@@ -191,9 +192,10 @@ function estruturar(fonte, aceitaFragmento, analisar, referencia) {
191
192
  comDelimitador = true;
192
193
  }
193
194
  if (linhasDeTabela > exports.TETO_DE_LINHAS_DE_TABELA) {
194
- return { fonte, secoes: [], links: [], textoDeMencao: [], frontmatter, frontmatterInvalido, tabelaGrande: true };
195
+ return { fonte, secoes: [], links: [], citacoes: [], textoDeMencao: [], frontmatter, frontmatterInvalido, tabelaGrande: true };
195
196
  }
196
197
  const secoes = [], links = [], semMencao = [];
198
+ const citacoes = [];
197
199
  const slug = contadorDeSlugs(), abertos = [];
198
200
  // B5: referencia de caractere pelo decodificador do micromark (as entidades do HTML5); a crua fica.
199
201
  const decodificada = (i, f) => referencia(corpo.slice(i + 1, f - 1)) ?? corpo.slice(i, f);
@@ -239,6 +241,10 @@ function estruturar(fonte, aceitaFragmento, analisar, referencia) {
239
241
  semMencao.push([e.inicio, e.fim]);
240
242
  }
241
243
  // Titulo, lido em paralelo: o texto dele junta dado, codigo, escape e entidade.
244
+ if (e.tipo === 'codeTextData' && !emExcesso)
245
+ citacoes.push({
246
+ destino: corpo.slice(e.inicio, e.fim), inicio: no(e.inicio), fim: no(e.fim),
247
+ });
242
248
  if (e.tipo === 'atxHeading' || e.tipo === 'setextHeading')
243
249
  titulo = { inicio: no(e.inicio), fim: no(e.fim), partes: [], noTexto: 0, foraDoTexto: 0, noCodigo: 0 };
244
250
  else if (titulo) {
@@ -304,7 +310,7 @@ function estruturar(fonte, aceitaFragmento, analisar, referencia) {
304
310
  }
305
311
  }
306
312
  const textoDeMencao = linhasDe(apagarTrechos(corpo, semMencao)).map((l) => ({ inicio: no(l.inicio), texto: l.texto }));
307
- return { fonte, secoes: secoes.sort((a, b) => a.inicio - b.inicio), links, textoDeMencao, frontmatter, frontmatterInvalido, tabelaGrande: false };
313
+ return { fonte, secoes: secoes.sort((a, b) => a.inicio - b.inicio), links, citacoes, textoDeMencao, frontmatter, frontmatterInvalido, tabelaGrande: false };
308
314
  }
309
315
  /** Caminho relativo a raiz a partir do arquivo do link; `null` se sair do repositorio. */
310
316
  function resolverCaminho(base, destino) {
@@ -323,6 +329,23 @@ function resolverCaminho(base, destino) {
323
329
  }
324
330
  return r.join('/');
325
331
  }
332
+ /** Citacao nao e resolucao de runtime: so caminho literal dentro do manifesto. */
333
+ function caminhoDaCitacao(base, literal, modulo = false) {
334
+ if (!literal || /[\s\\:#?%\u0000-\u001f\u007f]/u.test(literal) || literal.startsWith('/') || literal.startsWith('~'))
335
+ return null;
336
+ if (literal.split('/').some((p) => p === '.git' || p === '.orkastery'))
337
+ return null;
338
+ // Argumento sem prefixo de caminho e nome de pacote, nao candidato local.
339
+ if (modulo && !/^\.{1,2}\//.test(literal))
340
+ return null;
341
+ // Palavras e diretorios nao sao citacoes de arquivo. Modulos podem omitir extensao.
342
+ if (!modulo && (!literal.includes('/') || !/\.(?:[cm]?[jt]s|[jt]sx|json|md|markdown|ya?ml|toml|py|rs|go|c|h|css|html|svg|png|jpe?g|gif|webp|pdf|txt|sh|sql)$/i.test(literal)))
343
+ return null;
344
+ if (['', '.', '..'].includes(literal.split('/').at(-1)))
345
+ return null;
346
+ const caminho = literal.startsWith('./') || literal.startsWith('../') ? resolverCaminho(base, literal) : literal;
347
+ return caminho || null;
348
+ }
326
349
  const decodificar = (t) => {
327
350
  try {
328
351
  return decodeURIComponent(t);
@@ -339,7 +362,7 @@ function estruturarMarkdown(fonte, e) {
339
362
  mencoes.push({ inicio: l.inicio + m.index, id: m[0] });
340
363
  const d = s.frontmatter?.dados;
341
364
  return {
342
- path: fonte.path, secoes: s.secoes, links: s.links, mencoes,
365
+ path: fonte.path, secoes: s.secoes, links: s.links, citacoes: s.citacoes, mencoes,
343
366
  frontmatter: s.frontmatter && d
344
367
  ? { id: typeof d.id === 'string' ? d.id : null, tipo: typeof d.tipo === 'string' && d.tipo !== '', valores: s.frontmatter.valores }
345
368
  : null,
@@ -414,6 +437,13 @@ function ligarMarkdown(e) {
414
437
  };
415
438
  for (const s of estruturas) {
416
439
  const path = s.path, arquivo = { kind: 'file', path, fragment: null };
440
+ const citar = (alvo, k, metodo) => {
441
+ if (alvo === null || alvo === path || !arquivos.has(alvo))
442
+ return false;
443
+ aresta('cites', secaoEm(s, arquivo, k.inicio), { kind: 'file', path: alvo, fragment: null }, e.extratorMd, metodo, { path, inicio: k.inicio, fim: k.fim });
444
+ return true;
445
+ };
446
+ const linksReferenciados = [];
417
447
  for (const x of s.secoes) {
418
448
  if (x.slug === null) {
419
449
  lacuna('secao-recusada', path, x.inicio, null);
@@ -438,7 +468,15 @@ function ligarMarkdown(e) {
438
468
  lacuna('link-invalido', path, k.inicio, destino);
439
469
  continue;
440
470
  }
441
- const alvo = caminhoDecodificado === '' ? path : resolverCaminho(path, caminhoDecodificado);
471
+ const relativo = caminhoDecodificado === '' ? path : resolverCaminho(path, caminhoDecodificado);
472
+ // Links seguem o diretorio do documento; a citacao tambem admite caminho desde a raiz.
473
+ const literal = caminhoDaCitacao(path, caminhoDecodificado);
474
+ // Uma referencia ao arquivo ou a uma secao ja representa esta ocorrencia do link.
475
+ if (relativo === null || !arquivos.has(relativo)) {
476
+ if (citar(literal, k, 'explicit-link'))
477
+ linksReferenciados.push(k);
478
+ }
479
+ const alvo = relativo;
442
480
  if (alvo === null) {
443
481
  lacuna('link-fora-do-repositorio', path, k.inicio, destino);
444
482
  continue;
@@ -450,6 +488,7 @@ function ligarMarkdown(e) {
450
488
  const secoesDoAlvo = slugs.get(alvo);
451
489
  if (ancora && secoesDoAlvo?.has(ancora)) {
452
490
  aresta('references', origem, { kind: 'section', path: alvo, fragment: ancora }, e.extratorMd, 'explicit-link', t);
491
+ linksReferenciados.push(k);
453
492
  continue;
454
493
  }
455
494
  // P6: ancora que nao bate fica declarada; o link ainda prova a referencia ao arquivo.
@@ -458,6 +497,19 @@ function ligarMarkdown(e) {
458
497
  if (alvo === path)
459
498
  continue;
460
499
  aresta('references', origem, { kind: 'file', path: alvo, fragment: null }, e.extratorMd, 'explicit-link', t);
500
+ linksReferenciados.push(k);
501
+ }
502
+ // Codigo inline no rotulo pertence a mesma ocorrencia do link ja resolvido.
503
+ // Varredura por spans preserva citacoes independentes, inclusive na mesma linha.
504
+ linksReferenciados.sort((a, b) => a.inicio - b.inicio || b.fim - a.fim);
505
+ let link = 0;
506
+ for (const k of s.citacoes) {
507
+ while (link < linksReferenciados.length && linksReferenciados[link].fim <= k.inicio)
508
+ link++;
509
+ const l = linksReferenciados[link];
510
+ if (l && l.inicio <= k.inicio && k.fim <= l.fim)
511
+ continue;
512
+ citar(caminhoDaCitacao(path, k.destino), k, 'text-location');
461
513
  }
462
514
  const artefato = s.frontmatter && s.frontmatter.id !== null ? artefatos.get(s.frontmatter.id) : undefined;
463
515
  if (artefato && artefato.path === path && s.frontmatter) {
@@ -3,9 +3,26 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.OPCOES_TS_DESCRITAS = void 0;
4
4
  exports.casaSonda = casaSonda;
5
5
  exports.extrairTypeScript = extrairTypeScript;
6
+ const intelligence_graph_extract_md_1 = require("./intelligence-graph-extract-md");
6
7
  /** Extensoes que o extrator TS le; as do `extensaoDe` do KG2. */
7
8
  const EXTENSOES_DO_PROGRAMA = ['.cjs', '.cts', '.js', '.jsx', '.mjs', '.mts', '.ts', '.tsx'];
8
9
  const DECLARACOES_TS = ['.d.ts', '.d.mts', '.d.cts'];
10
+ /** KG5: candidatos literais; dist -> src e uma convencao de citacao, nunca prova de import. */
11
+ function candidatosDaCitacao(base) {
12
+ const formas = (p) => {
13
+ const semJs = p.replace(/\.(?:[cm]?js|jsx)$/, '');
14
+ if (semJs !== p)
15
+ return [semJs + '.ts', semJs + '.tsx', semJs + '.mts', semJs + '.cts'];
16
+ if (/\.[^/]+$/.test(p))
17
+ return [];
18
+ return EXTENSOES_DO_PROGRAMA.flatMap((ext) => [p + ext, p + '/index' + ext]);
19
+ };
20
+ const grupos = [[base], formas(base)];
21
+ const fonte = base.replace(/(^|\/)dist\//, '$1src/');
22
+ if (fonte !== base)
23
+ grupos.push([fonte], formas(fonte));
24
+ return grupos;
25
+ }
9
26
  /** A base sem a extensao (o TypeScript troca `.js` por `.ts`, e `.d.ts` conta inteira). */
10
27
  function semExtensao(base) {
11
28
  const nome = base.slice(base.lastIndexOf('/') + 1);
@@ -283,12 +300,19 @@ function extrairTypeScript(e, ts) {
283
300
  /** Fonte TypeScript, que o compilador transforma; JavaScript roda como esta. */
284
301
  const compiladoEm = (arquivo) => !EXTENSOES_JS.includes(extensao(arquivo));
285
302
  /** Chaves de todos os imports dentro de um no. */
286
- function especificadoresEm(raiz) {
303
+ function especificadoresEm(raiz, literais) {
287
304
  const r = [], compilado = compiladoEm(raiz.getSourceFile().fileName);
288
305
  const coletar = (n) => {
289
306
  const chave = chaveDoImport(n, compilado);
290
- if (chave !== null)
307
+ if (chave !== null) {
291
308
  r.push(chave);
309
+ const esp = ts.isImportDeclaration(n) || ts.isExportDeclaration(n) ? n.moduleSpecifier
310
+ : ts.isImportEqualsDeclaration(n) && ts.isExternalModuleReference(n.moduleReference) ? n.moduleReference.expression
311
+ : ts.isImportTypeNode(n) && ts.isLiteralTypeNode(n.argument) ? n.argument.literal
312
+ : ts.isCallExpression(n) ? n.arguments[0] : undefined;
313
+ if (esp)
314
+ literais?.set(esp, chave);
315
+ }
292
316
  ts.forEachChild(n, coletar);
293
317
  };
294
318
  coletar(raiz);
@@ -597,13 +621,15 @@ function extrairTypeScript(e, ts) {
597
621
  const divergentesGlobais = new Set();
598
622
  // KG4 (D3): por fonte, os caminhos a que as referencias de modulo resolvem e as bases das relativas.
599
623
  const dependenciasDe = new Map(), sondasDe = new Map();
624
+ const citacoesDe = new Map();
600
625
  for (const fonte of e.fontes) {
601
626
  const sf = programa.getSourceFile(absoluto(fonte.path));
602
627
  if (!sf || sf.fileName !== absoluto(fonte.path))
603
628
  continue;
604
629
  const ext = extensao(fonte.path), mapa = new Map();
605
630
  const dependencias = new Set(), sondas = new Set();
606
- for (const chave of especificadoresEm(sf)) {
631
+ const literaisDeModulo = new Map();
632
+ for (const chave of especificadoresEm(sf, literaisDeModulo)) {
607
633
  if (mapa.has(chave))
608
634
  continue;
609
635
  const esp = especificadorDaChave(chave), doCompilador = resolver(esp, fonte.path);
@@ -632,6 +658,46 @@ function extrairTypeScript(e, ts) {
632
658
  if (divergente)
633
659
  divergentesGlobais.add(`${fonte.path}\u0000${chave}`);
634
660
  }
661
+ // Strings so em testes/scripts. Comentarios, interpolacoes e concatenacoes nao sao avaliados.
662
+ const citacoes = [];
663
+ if (/(?:^|\/)(?:tests?|__tests__|scripts?)\/|\.(?:test|spec)\.[cm]?[jt]sx?$/.test(fonte.path)) {
664
+ const citar = (literal, n, modulo = false) => {
665
+ const base = (0, intelligence_graph_extract_md_1.caminhoDaCitacao)(fonte.path, literal, modulo);
666
+ if (base === null)
667
+ return;
668
+ const grupos = candidatosDaCitacao(base);
669
+ // Inclui ausentes e diretorios: a troca para arquivo invalida a unidade incremental.
670
+ // So arquivos presentes podem virar alvo; diretorios conservam apenas a sonda.
671
+ for (const grupo of grupos)
672
+ for (const p of grupo)
673
+ sondas.add(p);
674
+ for (const grupo of grupos) {
675
+ const presentes = grupo.filter((p) => arquivos.has(p));
676
+ if (!presentes.length)
677
+ continue;
678
+ if (presentes.length === 1 && presentes[0] !== fonte.path) {
679
+ dependencias.add(presentes[0]);
680
+ citacoes.push({ alvo: presentes[0], trecho: { path: fonte.path, inicio: n.getStart(sf), fim: n.getEnd() } });
681
+ }
682
+ break; // Ambiguidade nao autoriza escolher uma fonte nem tentar outro grupo.
683
+ }
684
+ };
685
+ const literais = (n) => {
686
+ if (ts.isStringLiteral(n) || ts.isNoSubstitutionTemplateLiteral(n)) {
687
+ const chave = literaisDeModulo.get(n);
688
+ // O resolver de import ja provou esta ocorrencia; outra string igual ainda pode citar.
689
+ if (chave !== undefined && mapa.get(chave)?.alvo != null)
690
+ return;
691
+ citar(n.text, n, chave !== undefined);
692
+ // Codigo citado por fixture: so argumentos literais de require/import dentro da string.
693
+ for (const m of n.text.matchAll(/\b(?:require|import)\s*\(\s*(['"])([^'"\r\n]+)\1\s*\)/g))
694
+ citar(m[2], n, true);
695
+ }
696
+ ts.forEachChild(n, literais);
697
+ };
698
+ literais(sf);
699
+ }
700
+ citacoesDe.set(fonte.path, citacoes);
635
701
  // O que o TypeScript coleta e a varredura nao ve (tipo importado no JSDoc) tambem liga o checker a outro arquivo.
636
702
  for (const nome of nomesDeModulo(sf)) {
637
703
  sondar(baseDaSonda(fonte.path, nome), sondas);
@@ -739,6 +805,8 @@ function extrairTypeScript(e, ts) {
739
805
  const aresta = (kind, from, to, t) => {
740
806
  saida.arestas.push({ kind, from, to, extrator: e.extrator, metodo: 'ast', trecho: t });
741
807
  };
808
+ for (const c of citacoesDe.get(fonte.path) ?? [])
809
+ aresta('cites', arquivo, { kind: 'file', path: c.alvo, fragment: null }, c.trecho);
742
810
  const lacuna = (categoria, n, detalhe = null) => {
743
811
  saida.lacunas.push({ categoria, path: fonte.path, inicio: n ? n.getStart(sf) : null, detalhe });
744
812
  };
@@ -46,10 +46,12 @@ exports.carregarAnalisadores = carregarAnalisadores;
46
46
  * `typescript` e os pacotes do micromark so valem dentro da instalacao do `ork` que roda (o
47
47
  * `node_modules` do pacote `@orkastery/cli` que contem este modulo), nunca do diretorio atual, do
48
48
  * `NODE_PATH` nem de uma pasta acima, como a do projeto que instalou o `ork` como dependencia: o
49
- * repositorio lido e dado, e codigo dele nunca e carregado. Eles nao sao dependencias de runtime do
50
- * pacote publicado (D1: dependencia nova e decisao de produto); onde faltam ou estao fora da
51
- * instalacao, a recusa e `grafo.parser.indisponivel: <pacote>`. `pacotesDosAnalisadores` lista o
52
- * fecho de dependencias deles, com versao, para a chave do indice (CHECK rodada 1, A10).
49
+ * repositorio lido e dado, e codigo dele nunca e carregado. Desde a correcao de empacotamento da
50
+ * RM-031 eles sao dependencias de runtime do pacote publicado, com versao exata, e o
51
+ * `npm install -g` os poe dentro da instalacao; onde faltam ou estao fora dela (o `ork` instalado
52
+ * dentro de um projeto ou pelo `npx`, que icam as dependencias), a recusa e
53
+ * `grafo.parser.indisponivel: <pacote>`, e o `ork doctor` diz a correcao. `pacotesDosAnalisadores`
54
+ * lista o fecho de dependencias deles, com versao, para a chave do indice (CHECK rodada 1, A10).
53
55
  *
54
56
  * O juiz de sintaxe e o V8 do Node que roda a extracao, o mesmo que carregaria o arquivo. CommonJS
55
57
  * compila aqui, em funcao com as variaveis do modulo, como o carregador faz. ESM compila num unico
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.rotuloDoNo = exports.ErroDeConsulta = exports.AVISO_DE_PARCIALIDADE = exports.CANDIDATOS_LISTADOS = exports.LIMITE_MAXIMO = exports.LIMITE_PADRAO = exports.PROFUNDIDADE_MAXIMA = exports.CONSULTA_SCHEMA = void 0;
3
+ exports.rotuloDoNo = exports.ErroDeConsulta = exports.TUDO_IGUAL = exports.AVISO_DE_PARCIALIDADE = exports.CANDIDATOS_LISTADOS = exports.LIMITE_MAXIMO = exports.LIMITE_PADRAO = exports.PROFUNDIDADE_MAXIMA = exports.CONSULTA_SCHEMA = void 0;
4
4
  exports.filtrarGrafo = filtrarGrafo;
5
5
  exports.prepararConsulta = prepararConsulta;
6
6
  exports.resolverNo = resolverNo;
@@ -23,6 +23,10 @@ exports.textoDaResposta = textoDaResposta;
23
23
  *
24
24
  * D7: a resposta e a mesma para o mesmo grafo e a mesma pergunta, byte a byte, em texto e em JSON
25
25
  * (`ork.code-graph-query/v0`, provisorio e fora do contrato): ordem por code point, sem horario.
26
+ *
27
+ * RM-031 KG5 fatia 5 (D2 a D4, D9, D10): a resposta traz `fontes`, o sha256 e o blob de cada caminho
28
+ * citado, e cada aresta diz se as fontes dela conferem com a arvore. Quem chama passa a situacao de
29
+ * cada caminho (o CLI le os bytes); sem ela, tudo vale `igual`, e a consulta continua sem arquivo.
26
30
  */
27
31
  const intelligence_graph_contract_1 = require("./intelligence-graph-contract");
28
32
  exports.CONSULTA_SCHEMA = 'ork.code-graph-query/v0';
@@ -32,6 +36,9 @@ exports.LIMITE_MAXIMO = 10_000;
32
36
  /** Candidatos listados quando um nome solto e ambiguo. */
33
37
  exports.CANDIDATOS_LISTADOS = 20;
34
38
  exports.AVISO_DE_PARCIALIDADE = 'parcial: so arestas que o extrator prova; chamada por despacho de tipo, import nao resolvido e as lacunas do relatorio de extracao ficam fora';
39
+ /** Sem saber da arvore, toda fonte vale `igual`: e o que a arvore limpa garante. */
40
+ const TUDO_IGUAL = () => 'igual';
41
+ exports.TUDO_IGUAL = TUDO_IGUAL;
35
42
  /** Erro tipado da consulta; `candidatos` so no nome ambiguo, e so com nos visiveis. */
36
43
  class ErroDeConsulta extends Error {
37
44
  codigo;
@@ -101,7 +108,11 @@ function prepararConsulta(grafoInteiro, concessao) {
101
108
  lista.sort(ordem((a) => rotulos.get(a.from)));
102
109
  for (const lista of porFragmento.values())
103
110
  lista.sort((x, y) => (0, intelligence_graph_contract_1.compararUtf8)((0, exports.rotuloDoNo)(x), (0, exports.rotuloDoNo)(y)));
104
- return { repository_id: grafo.repository_id, snapshot_id: grafo.snapshot.snapshot_id, nos, rotulos, porLocalizador, arquivos, porFragmento, saida, entrada };
111
+ const fontes = new Map();
112
+ for (const m of grafo.snapshot.source_manifest)
113
+ if (visivel(m.access, concessao))
114
+ fontes.set(m.path, { source_hash: m.source_hash, source_version: m.source_version });
115
+ return { repository_id: grafo.repository_id, snapshot_id: grafo.snapshot.snapshot_id, nos, rotulos, porLocalizador, arquivos, porFragmento, saida, entrada, fontes };
105
116
  }
106
117
  function ambiguo(texto, candidatos) {
107
118
  const rotulos = candidatos.map(exports.rotuloDoNo).sort(intelligence_graph_contract_1.compararUtf8);
@@ -169,6 +180,31 @@ function arestaDaResposta(g, a, distancia) {
169
180
  || (0, intelligence_graph_contract_1.compararUtf8)(x.extractor_id, y.extractor_id) || (0, intelligence_graph_contract_1.compararUtf8)(x.extraction_method, y.extraction_method));
170
181
  return { kind: a.kind, from: g.rotulos.get(a.from), to: g.rotulos.get(a.to), edge_id: a.edge_id, distancia, evidencias };
171
182
  }
183
+ /**
184
+ * KG5 fatia 5 (D4): a aresta so e `igual` quando as fontes dela inteiras conferem: o arquivo da origem, o
185
+ * do alvo (calls e imports resolvem o alvo nele) e os das evidencias. So as devolvidas pedem a situacao.
186
+ * Com teto, a busca binaria monta respostas com limites diferentes, e o CLI le os arquivos citados pela
187
+ * maior delas, a do limite pedido, mesmo os que o corte tira (CHECK rodada 1, S4).
188
+ */
189
+ function comArvore(g, a, r, situacao) {
190
+ const caminhos = [g.nos.get(a.from).locator.path, g.nos.get(a.to).locator.path, ...r.evidencias.map((e) => e.path)];
191
+ const { evidencias, ...resto } = r;
192
+ return { ...resto, arvore: caminhos.every((p) => situacao(p) === 'igual') ? 'igual' : 'modificada', evidencias };
193
+ }
194
+ /** KG5 fatia 5 (D2, D10): uma entrada por caminho dos nos e das evidencias da resposta, em ordem UTF-8. */
195
+ function fontesDaResposta(g, nos, arestas, situacao) {
196
+ const caminhos = new Set(nos.map((n) => n.path));
197
+ for (const a of arestas)
198
+ for (const e of a.evidencias)
199
+ caminhos.add(e.path);
200
+ const fontes = [];
201
+ for (const p of [...caminhos].sort(intelligence_graph_contract_1.compararUtf8)) {
202
+ const f = g.fontes.get(p);
203
+ if (f)
204
+ fontes.push({ path: p, source_hash: f.source_hash, source_version: f.source_version, arvore: situacao(p) });
205
+ }
206
+ return fontes;
207
+ }
172
208
  /** CHECK rodada 1 (A2): primeiro as arestas mais perto do alvo, e o limite corta as mais longe. */
173
209
  const ordemDasArestas = (x, y) => (x.distancia - y.distancia)
174
210
  || (0, intelligence_graph_contract_1.compararUtf8)(x.kind, y.kind) || (0, intelligence_graph_contract_1.compararUtf8)(x.from, y.from) || (0, intelligence_graph_contract_1.compararUtf8)(x.to, y.to) || (0, intelligence_graph_contract_1.compararUtf8)(x.edge_id, y.edge_id);
@@ -204,7 +240,7 @@ function tiposValidos(tipos) {
204
240
  * cada uma com a distancia do no que a explorou. `limite` corta pela distancia e pela ordem fixa, e
205
241
  * a lista de nos so traz o alvo e as pontas das arestas devolvidas.
206
242
  */
207
- function vizinhanca(g, cabecalho, tipo, texto, opcoes) {
243
+ function vizinhanca(g, cabecalho, tipo, texto, opcoes, situacao) {
208
244
  const profundidade = inteiroEntre('profundidade', opcoes.profundidade ?? 1, 1, exports.PROFUNDIDADE_MAXIMA);
209
245
  const limite = inteiroEntre('limite', opcoes.limite ?? exports.LIMITE_PADRAO, 1, exports.LIMITE_MAXIMO);
210
246
  const sentido = opcoes.sentido ?? 'ambos';
@@ -228,8 +264,8 @@ function vizinhanca(g, cabecalho, tipo, texto, opcoes) {
228
264
  }
229
265
  camada = proxima;
230
266
  }
231
- const todas = [...vistas.values()].map(({ aresta, d }) => arestaDaResposta(g, aresta, d)).sort(ordemDasArestas);
232
- const devolvidas = todas.slice(0, limite);
267
+ const todas = [...vistas.values()].map(({ aresta, d }) => ({ aresta, r: arestaDaResposta(g, aresta, d) })).sort((x, y) => ordemDasArestas(x.r, y.r));
268
+ const devolvidas = todas.slice(0, limite).map(({ aresta, r }) => comArvore(g, aresta, r, situacao));
233
269
  const idPorRotulo = new Map([...distancia.keys()].map((id) => [g.rotulos.get(id), id]));
234
270
  const pontas = new Set([alvo.node_id, ...devolvidas.flatMap((a) => [idPorRotulo.get(a.from), idPorRotulo.get(a.to)])]);
235
271
  const nos = [...pontas].map((id) => noDaResposta(g, g.nos.get(id), distancia.get(id)))
@@ -239,31 +275,31 @@ function vizinhanca(g, cabecalho, tipo, texto, opcoes) {
239
275
  consulta: { tipo, alvo: texto, para: null, profundidade, sentido, tipos, limite },
240
276
  indice: cabecalho, alvo: noDaResposta(g, alvo, 0), para: null, nos, total_nos: distancia.size,
241
277
  arestas: devolvidas, total_arestas: todas.length, truncado: todas.length > limite,
242
- caminho: null, nos_explorados: null, parcial: exports.AVISO_DE_PARCIALIDADE,
278
+ caminho: null, nos_explorados: null, fontes: fontesDaResposta(g, nos, devolvidas, situacao), parcial: exports.AVISO_DE_PARCIALIDADE,
243
279
  };
244
280
  }
245
- function vizinhos(g, cabecalho, alvo, opcoes = {}) {
246
- return vizinhanca(g, cabecalho, 'vizinhos', alvo, opcoes);
281
+ function vizinhos(g, cabecalho, alvo, opcoes = {}, situacao = exports.TUDO_IGUAL) {
282
+ return vizinhanca(g, cabecalho, 'vizinhos', alvo, opcoes, situacao);
247
283
  }
248
284
  /** Quem chama o simbolo: as arestas `calls` que chegam, ate `profundidade` saltos. */
249
- function chamadores(g, cabecalho, alvo, opcoes = {}) {
285
+ function chamadores(g, cabecalho, alvo, opcoes = {}, situacao = exports.TUDO_IGUAL) {
250
286
  if (resolverNo(g, alvo).kind !== 'symbol')
251
287
  throw new ErroDeConsulta('grafo.consulta.alvo-invalido', 'chamadores pede um simbolo; para arquivo, use importadores ou vizinhos');
252
- return vizinhanca(g, cabecalho, 'chamadores', alvo, { ...opcoes, sentido: 'entrada', tipos: ['calls'] });
288
+ return vizinhanca(g, cabecalho, 'chamadores', alvo, { ...opcoes, sentido: 'entrada', tipos: ['calls'] }, situacao);
253
289
  }
254
290
  /** Quem importa o arquivo ou o simbolo: as arestas `imports` que chegam, ate `profundidade` saltos. */
255
- function importadores(g, cabecalho, alvo, opcoes = {}) {
291
+ function importadores(g, cabecalho, alvo, opcoes = {}, situacao = exports.TUDO_IGUAL) {
256
292
  const kind = resolverNo(g, alvo).kind;
257
293
  if (kind !== 'file' && kind !== 'symbol')
258
294
  throw new ErroDeConsulta('grafo.consulta.alvo-invalido', 'importadores pede um arquivo ou um simbolo');
259
- return vizinhanca(g, cabecalho, 'importadores', alvo, { ...opcoes, sentido: 'entrada', tipos: ['imports'] });
295
+ return vizinhanca(g, cabecalho, 'importadores', alvo, { ...opcoes, sentido: 'entrada', tipos: ['imports'] }, situacao);
260
296
  }
261
297
  /**
262
298
  * D5: o menor caminho de `de` a `para`, pelas arestas no sentido pedido (padrao: `saida`, a direcao
263
299
  * da aresta). Busca em largura com a expansao na ordem fixa: entre caminhos do mesmo tamanho, sai
264
300
  * sempre o mesmo. Sem caminho, `caminho` e `null`, com os nos explorados.
265
301
  */
266
- function caminho(g, cabecalho, de, para, opcoes = {}) {
302
+ function caminho(g, cabecalho, de, para, opcoes = {}, situacao = exports.TUDO_IGUAL) {
267
303
  const sentido = opcoes.sentido ?? 'saida';
268
304
  if (!['entrada', 'saida', 'ambos'].includes(sentido))
269
305
  throw new ErroDeConsulta('grafo.consulta.uso', `sentido desconhecido: ${sentido}`);
@@ -284,20 +320,20 @@ function caminho(g, cabecalho, de, para, opcoes = {}) {
284
320
  if (anterior.has(destino.node_id)) {
285
321
  const cadeia = [];
286
322
  for (let id = destino.node_id, atual = anterior.get(id); atual; id = atual.de, atual = anterior.get(id)) {
287
- cadeia.push({ id, passo: { sentido: atual.sentido, aresta: arestaDaResposta(g, atual.aresta, 0) } });
323
+ cadeia.push({ id, sentido: atual.sentido, aresta: atual.aresta });
288
324
  }
289
325
  cadeia.reverse();
290
- cadeia.forEach((c, n) => { c.passo.aresta.distancia = n; });
291
- passos = cadeia.map((c) => c.passo);
326
+ passos = cadeia.map((c, n) => ({ sentido: c.sentido, aresta: comArvore(g, c.aresta, arestaDaResposta(g, c.aresta, n), situacao) }));
292
327
  nos = [origem.node_id, ...cadeia.map((c) => c.id)].map((id, d) => noDaResposta(g, g.nos.get(id), d));
293
328
  }
329
+ const arestas = passos ? passos.map((p) => p.aresta) : [];
294
330
  return {
295
331
  schema: exports.CONSULTA_SCHEMA,
296
332
  consulta: { tipo: 'caminho', alvo: de, para, profundidade: null, sentido, tipos, limite: null },
297
333
  indice: cabecalho, alvo: noDaResposta(g, origem, 0), para: noDaResposta(g, destino, passos ? passos.length : null),
298
334
  nos, total_nos: nos.length,
299
- arestas: passos ? passos.map((p) => p.aresta) : [], total_arestas: passos ? passos.length : 0, truncado: false,
300
- caminho: passos, nos_explorados: anterior.size, parcial: exports.AVISO_DE_PARCIALIDADE,
335
+ arestas, total_arestas: arestas.length, truncado: false,
336
+ caminho: passos, nos_explorados: anterior.size, fontes: fontesDaResposta(g, nos, arestas, situacao), parcial: exports.AVISO_DE_PARCIALIDADE,
301
337
  };
302
338
  }
303
339
  function jsonDaResposta(r) {
@@ -310,8 +346,18 @@ function linhasDaEvidencia(e) {
310
346
  : `${e.path} pagina ${s.page}`;
311
347
  return ` ${e.extractor_id} ${e.extraction_method} ${onde} bytes ${s.byte_start}-${s.byte_end}`;
312
348
  }
349
+ /** KG5 fatia 5 (D9): a aresta cuja fonte mudou diz isso na propria linha, como o JSON diz no campo `arvore`. */
313
350
  function linhasDaAresta(a, prefixo = ' ') {
314
- return [`${prefixo}${a.kind} ${a.from} -> ${a.to}`, ...a.evidencias.map(linhasDaEvidencia)];
351
+ const marca = a.arvore === 'modificada' ? ' [fonte modificada na arvore]' : '';
352
+ return [`${prefixo}${a.kind} ${a.from} -> ${a.to}${marca}`, ...a.evidencias.map(linhasDaEvidencia)];
353
+ }
354
+ /** KG5 fatia 5 (D9): ate cinco fontes que nao tem na arvore os bytes do indice, com a situacao de cada uma. */
355
+ function linhaDasFontesMudadas(fontes) {
356
+ const mudaram = fontes.filter((f) => f.arvore !== 'igual');
357
+ if (!mudaram.length)
358
+ return [];
359
+ const resto = mudaram.length > 5 ? ` e mais ${mudaram.length - 5}` : '';
360
+ return [`fontes que mudaram na arvore: ${mudaram.slice(0, 5).map((f) => `${f.path} (${f.arvore})`).join(', ')}${resto}`];
315
361
  }
316
362
  function textoDaResposta(r) {
317
363
  const c = r.consulta, i = r.indice;
@@ -320,6 +366,7 @@ function textoDaResposta(r) {
320
366
  ];
321
367
  if (i.arvore === 'modificada')
322
368
  linhas.push('aviso: a arvore tem mudanca rastreada; a resposta e do HEAD, nao da arvore');
369
+ linhas.push(...linhaDasFontesMudadas(r.fontes));
323
370
  linhas.push(`extratores: ${i.extratores.map((e) => `${e.extractor_id} ${e.extractor_version}`).join(', ')}`);
324
371
  if (c.tipo === 'caminho') {
325
372
  const para = r.para;