@orkastery/cli 0.4.2 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. package/adapters/README.md +11 -0
  2. package/adapters/claude-code/.claude-plugin/plugin.json +3 -1
  3. package/adapters/claude-code/agents/ork-check.md +1 -1
  4. package/adapters/claude-code/agents/ork-go.md +2 -2
  5. package/adapters/claude-code/agents/ork-goal.md +1 -1
  6. package/adapters/claude-code/agents/ork-master.md +1 -1
  7. package/adapters/claude-code/agents/ork-plan.md +1 -1
  8. package/adapters/claude-code/agents/ork-ship.md +1 -1
  9. package/adapters/claude-code/commands/master.md +11 -8
  10. package/adapters/claude-code/commands/ork.md +8 -0
  11. package/adapters/claude-code/commands/ship.md +2 -2
  12. package/adapters/codex/skills/ork/SKILL.md +12 -1
  13. package/adapters/hermes/README.md +3 -0
  14. package/adapters/hermes/bin/ork-abrir-thread.sh +17 -2
  15. package/adapters/hermes/bin/ork-brain.sh +3 -0
  16. package/adapters/hermes/bin/ork-maestro.sh +4 -1
  17. package/adapters/hermes/bin/ork-network-roadmap.sh +11 -0
  18. package/adapters/hermes/bin/ork-roadmap-status.sh +9 -0
  19. package/adapters/hermes/hermes.plugin.json +2 -0
  20. package/adapters/hermes/hitl-ingress/__init__.py +36 -3
  21. package/adapters/hermes/skills/orkastery-devmaster/SKILL.md +19 -20
  22. package/adapters/openclaw/README.md +34 -3
  23. package/adapters/openclaw/dist/hitl-ingress.js +37 -3
  24. package/adapters/openclaw/dist/index.js +92 -39
  25. package/adapters/openclaw/openclaw.plugin.json +10 -3
  26. package/adapters/openclaw/package.json +2 -2
  27. package/adapters/openclaw/src/hitl-ingress.ts +32 -3
  28. package/adapters/openclaw/src/index.ts +108 -41
  29. package/assets/docs/padroes/documentacao-de-produto.md +2 -2
  30. package/assets/docs/padroes/roadmap-de-produto.md +1 -1
  31. package/assets/docs/produto/_modelo-feature.md +2 -0
  32. package/assets/orkmind_bridge.py +214 -1
  33. package/dist/adapters/claude-bg.js +88 -9
  34. package/dist/adapters/codex-events.js +3 -1
  35. package/dist/board.js +74 -0
  36. package/dist/branch-de-estado.js +5 -4
  37. package/dist/busca-semantica.js +161 -0
  38. package/dist/ci.js +141 -10
  39. package/dist/company-brain-cli.js +27 -2
  40. package/dist/company-brain-context.js +132 -0
  41. package/dist/company-brain-dossie.js +376 -0
  42. package/dist/company-brain-mcp.js +4 -1
  43. package/dist/conducao.js +117 -31
  44. package/dist/decisao-autonoma.js +42 -0
  45. package/dist/docs.js +64 -22
  46. package/dist/doctor.js +33 -0
  47. package/dist/entrega-pr.js +109 -5
  48. package/dist/experiencia-instalacao.js +256 -0
  49. package/dist/experiencia.js +72 -0
  50. package/dist/fabrica-estado.js +43 -10
  51. package/dist/fechamento.js +105 -0
  52. package/dist/fix.js +1 -1
  53. package/dist/forja.js +435 -0
  54. package/dist/gates.js +10 -1
  55. package/dist/hitl-classificacao.js +11 -1
  56. package/dist/hitl-contract.js +50 -7
  57. package/dist/hitl-curto.js +158 -0
  58. package/dist/hitl-gates.js +67 -1
  59. package/dist/hitl-lote.js +30 -15
  60. package/dist/hitl-presentation.js +10 -7
  61. package/dist/hitl-resumo.js +33 -12
  62. package/dist/hitl-texto-livre.js +150 -0
  63. package/dist/hosts.js +86 -1
  64. package/dist/index.js +605 -41
  65. package/dist/indice-vetorial.js +317 -0
  66. package/dist/init.js +13 -0
  67. package/dist/integracoes-locais.js +1 -1
  68. package/dist/intelligence-benchmark-contract.js +570 -0
  69. package/dist/intelligence-graph-cli.js +430 -0
  70. package/dist/intelligence-graph-contract.js +577 -0
  71. package/dist/intelligence-graph-extract-md.js +469 -0
  72. package/dist/intelligence-graph-extract-ts.js +810 -0
  73. package/dist/intelligence-graph-extract.js +369 -0
  74. package/dist/intelligence-graph-index.js +512 -0
  75. package/dist/intelligence-graph-parsers.js +327 -0
  76. package/dist/intelligence-graph-query.js +344 -0
  77. package/dist/intelligence-graph-repo.js +187 -0
  78. package/dist/leases.js +84 -0
  79. package/dist/ledger.js +14 -0
  80. package/dist/maestro-cli.js +10 -1
  81. package/dist/maestro-contract.js +5 -1
  82. package/dist/maestro-discovery.js +16 -2
  83. package/dist/maestro-snapshot.js +6 -2
  84. package/dist/manifest.js +142 -5
  85. package/dist/master-batch.js +3 -2
  86. package/dist/master-digest.js +2 -2
  87. package/dist/master-nota.js +203 -0
  88. package/dist/master.js +4 -0
  89. package/dist/mcp-experiencia.js +164 -0
  90. package/dist/mcp-git.js +36 -10
  91. package/dist/mcp-install.js +3 -1
  92. package/dist/mcp-maestro.js +3 -2
  93. package/dist/mcp-server.js +84 -5
  94. package/dist/memoria.js +147 -1
  95. package/dist/network-roadmap.js +907 -0
  96. package/dist/ocupacao.js +38 -2
  97. package/dist/onboarding.js +113 -7
  98. package/dist/orkmind.js +165 -9
  99. package/dist/phase.js +166 -13
  100. package/dist/policies.js +2 -0
  101. package/dist/projeto-alvo.js +551 -0
  102. package/dist/prompts.js +1 -1
  103. package/dist/pulse-consentimento.js +4 -2
  104. package/dist/pulse-delivery.js +5 -0
  105. package/dist/pulse-resposta.js +674 -49
  106. package/dist/pulse.js +21 -0
  107. package/dist/ratelimit.js +3 -2
  108. package/dist/retry.js +230 -9
  109. package/dist/roadmap-reservas.js +262 -20
  110. package/dist/roadmap-status.js +213 -0
  111. package/dist/runtime-ambiente.js +12 -1
  112. package/dist/session-watcher-claude.js +26 -0
  113. package/dist/sessoes.js +49 -15
  114. package/dist/thread-close.js +3 -0
  115. package/dist/verify.js +5 -4
  116. package/dist/worktree.js +77 -0
  117. package/eval/casos/orchestration-experience-pt-br.json +48 -0
  118. package/eval/casos/orchestration-experience.json +48 -0
  119. package/package.json +1 -1
  120. package/schemas/code-artifact-graph.v1.schema.json +581 -0
  121. package/schemas/graph-benchmark.v1.schema.json +2240 -0
  122. package/schemas/maestro-snapshot.schema.json +23 -0
  123. package/skills/README.md +7 -5
  124. package/skills/core/onboarding/SKILL.md +7 -0
  125. package/skills/core/orchestration-experience/SKILL.md +113 -0
  126. package/skills/core/orchestration-experience-pt-br/SKILL.md +112 -0
  127. package/skills/core/orkastery-bootstrap/SKILL.md +2 -2
  128. package/skills/phases/go-implementation/SKILL.md +2 -0
@@ -0,0 +1,327 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.PACOTES_DOS_ANALISADORES = void 0;
37
+ exports.pacotesDosAnalisadores = pacotesDosAnalisadores;
38
+ exports.versoesDosAnalisadores = versoesDosAnalisadores;
39
+ exports.criarJuizDeSintaxe = criarJuizDeSintaxe;
40
+ exports.carregarAnalisadores = carregarAnalisadores;
41
+ /**
42
+ * RM-031 KG3 (D1): os analisadores do extrator do grafo, carregados da instalacao do `ork` que
43
+ * roda. Sucede o adaptador do micromark e o juiz de sintaxe que o comando provisorio do KG2 levava
44
+ * em `core/scripts/` (KG2 D15 e D16), com o mesmo comportamento e as mesmas versoes.
45
+ *
46
+ * `typescript` e os pacotes do micromark so valem dentro da instalacao do `ork` que roda (o
47
+ * `node_modules` do pacote `@orkastery/cli` que contem este modulo), nunca do diretorio atual, do
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).
53
+ *
54
+ * O juiz de sintaxe e o V8 do Node que roda a extracao, o mesmo que carregaria o arquivo. CommonJS
55
+ * compila aqui, em funcao com as variaveis do modulo, como o carregador faz. ESM compila num unico
56
+ * processo filho por chamada, com `vm.SourceTextModule`, que so analisa o modulo, sem ligar nem
57
+ * avaliar. O filho roda sem ambiente e com argumentos fixos; o veredito volta estruturado no stdout
58
+ * e o stderr nunca e lido. Nada e executado.
59
+ */
60
+ const node_child_process_1 = require("node:child_process");
61
+ const node_crypto_1 = require("node:crypto");
62
+ const fs = __importStar(require("node:fs"));
63
+ const path = __importStar(require("node:path"));
64
+ const vm = __importStar(require("node:vm"));
65
+ /** Os unicos pacotes que o nucleo carrega para extrair: o compilador e o micromark com a tabela GFM. */
66
+ exports.PACOTES_DOS_ANALISADORES = [
67
+ 'typescript', 'micromark', 'micromark-extension-gfm-table', 'decode-named-character-reference',
68
+ 'micromark-util-decode-numeric-character-reference',
69
+ ];
70
+ const NOME_DA_INSTALACAO = '@orkastery/cli';
71
+ function lerJson(arquivo) {
72
+ try {
73
+ return JSON.parse(fs.readFileSync(arquivo, 'utf8'));
74
+ }
75
+ catch {
76
+ return null;
77
+ }
78
+ }
79
+ /** A raiz do pacote `@orkastery/cli` que contem este modulo: `dist/` ou `dist-test/src/` ficam abaixo dela. */
80
+ function raizDaInstalacao() {
81
+ for (let dir = __dirname;; dir = path.dirname(dir)) {
82
+ if (lerJson(path.join(dir, 'package.json'))?.name === NOME_DA_INSTALACAO)
83
+ return dir;
84
+ if (path.dirname(dir) === dir)
85
+ throw new Error('grafo.parser.indisponivel: instalacao do ork nao encontrada');
86
+ }
87
+ }
88
+ /**
89
+ * O `node_modules` da instalacao pelo caminho real: o `require.resolve` devolve o caminho real do
90
+ * pacote, e o `node_modules` pode ser link simbolico (clone com as dependencias ligadas, pnpm).
91
+ * CHECK rodada 2 (R2-B1): comparar o caminho sem resolver recusava a instalacao inteira.
92
+ */
93
+ function modulosDaInstalacao(raiz) {
94
+ try {
95
+ return fs.realpathSync(path.join(raiz, 'node_modules'));
96
+ }
97
+ catch {
98
+ return null;
99
+ }
100
+ }
101
+ /** Sem `node_modules` na instalacao, nada esta dentro dela. */
102
+ const dentro = (modulos, alvo) => modulos !== null && alvo.startsWith(modulos + path.sep);
103
+ /**
104
+ * Resolve a entrada do pacote a partir da instalacao e sobe ate o `package.json` com o nome dele,
105
+ * sem sair do pacote. O `exports` do micromark nao expoe o `package.json`, e um `package.json` de
106
+ * subpasta (`{ "type": "module" }`) nao tem nome. Entrada fora do `node_modules` da instalacao
107
+ * (pasta acima, `NODE_PATH`, pasta global) e recusada antes de qualquer leitura dela.
108
+ */
109
+ function pacoteDe(nome) {
110
+ const raiz = raizDaInstalacao();
111
+ let entrada;
112
+ try {
113
+ entrada = require.resolve(nome, { paths: [raiz] });
114
+ }
115
+ catch {
116
+ throw new Error(`grafo.parser.indisponivel: ${nome}`);
117
+ }
118
+ if (!dentro(modulosDaInstalacao(raiz), entrada))
119
+ throw new Error(`grafo.parser.indisponivel: ${nome} fora da instalacao do ork`);
120
+ for (let dir = path.dirname(entrada); path.basename(dir) !== 'node_modules' && path.dirname(dir) !== dir; dir = path.dirname(dir)) {
121
+ const dados = lerJson(path.join(dir, 'package.json'));
122
+ if (dados && dados.name === nome && typeof dados.version === 'string')
123
+ return { entrada, dir, versao: dados.version };
124
+ }
125
+ throw new Error(`grafo.parser.indisponivel: ${nome} sem package.json`);
126
+ }
127
+ /**
128
+ * O `package.json` da dependencia `dep` vista de `de`, pela busca do Node, sem sair da instalacao.
129
+ * Tudo em caminho real: `de` vem do `require.resolve`, e o topo e a pasta real que contem o
130
+ * `node_modules` da instalacao.
131
+ */
132
+ function pacoteInstalado(modulos, dep, de) {
133
+ if (modulos === null)
134
+ return null;
135
+ const topo = path.dirname(modulos);
136
+ for (let dir = de; dir === topo || dir.startsWith(topo + path.sep); dir = path.dirname(dir)) {
137
+ if (path.basename(dir) !== 'node_modules') {
138
+ const arquivo = path.join(dir, 'node_modules', dep, 'package.json');
139
+ if (fs.existsSync(arquivo)) {
140
+ const real = fs.realpathSync(arquivo);
141
+ return dentro(modulos, real) ? real : null;
142
+ }
143
+ }
144
+ if (dir === topo)
145
+ break;
146
+ }
147
+ return null;
148
+ }
149
+ /**
150
+ * CHECK rodada 1 (A10): o fecho de dependencias dos analisadores, `nome@versao` ordenado. O parser
151
+ * de CommonMark (`micromark-core-commonmark`) e os utilitarios mudam o grafo sem mudar a versao do
152
+ * micromark: a chave do indice precisa deles.
153
+ */
154
+ function pacotesDosAnalisadores() {
155
+ const modulos = modulosDaInstalacao(raizDaInstalacao()), vistos = new Map();
156
+ const fila = exports.PACOTES_DOS_ANALISADORES.map((p) => path.join(pacoteDe(p).dir, 'package.json'));
157
+ while (fila.length) {
158
+ const arquivo = fila.shift();
159
+ const dados = lerJson(arquivo);
160
+ if (!dados || typeof dados.name !== 'string' || typeof dados.version !== 'string')
161
+ throw new Error(`grafo.parser.indisponivel: ${arquivo} ilegivel`);
162
+ const chave = `${dados.name}@${dados.version}`;
163
+ if (vistos.has(arquivo))
164
+ continue;
165
+ vistos.set(arquivo, chave);
166
+ const obrigatorias = new Set(Object.keys(dados.dependencies ?? {}));
167
+ const deps = [...new Set([...obrigatorias, ...Object.keys(dados.optionalDependencies ?? {})])].sort();
168
+ for (const dep of deps) {
169
+ const achado = pacoteInstalado(modulos, dep, path.dirname(arquivo));
170
+ if (achado && !vistos.has(achado))
171
+ fila.push(achado);
172
+ // Opcional que nao veio e normal; obrigatoria que falta dentro da instalacao fica marcada, e a carga recusa.
173
+ else if (!achado && obrigatorias.has(dep))
174
+ vistos.set(`${arquivo}\u0000${dep}`, `${dep}@ausente`);
175
+ }
176
+ }
177
+ return [...new Set(vistos.values())].sort();
178
+ }
179
+ function versoesDosAnalisadores() {
180
+ const versoes = Object.fromEntries(exports.PACOTES_DOS_ANALISADORES.map((p) => [p, pacoteDe(p).versao]));
181
+ return {
182
+ typescript: versoes.typescript,
183
+ javascript: `node.${process.versions.node}`,
184
+ markdown: `micromark.${versoes.micromark}.gfm-table.${versoes['micromark-extension-gfm-table']}`,
185
+ unicode: String(process.versions.unicode),
186
+ };
187
+ }
188
+ /** O micromark e so ESM: o `require` de ESM do Node carrega; onde ele nao existe, a recusa e tipada. */
189
+ function carregar(nome) {
190
+ const { entrada } = pacoteDe(nome);
191
+ try {
192
+ return require(entrada);
193
+ }
194
+ catch (e) {
195
+ throw new Error(`grafo.parser.indisponivel: ${nome} (${e.code ?? 'erro ao carregar'})`);
196
+ }
197
+ }
198
+ /**
199
+ * KG2 D15: `analisar(texto)` devolve os eventos do micromark com o offset no texto (unidades UTF-16);
200
+ * `referencia(valor)` decodifica uma referencia de caractere (`eacute`, `#233`, `#xE9`) com as
201
+ * funcoes que o micromark usa ao gerar HTML, e devolve `null` se o nome nao e entidade do HTML5.
202
+ */
203
+ function carregarMarkdown(versao) {
204
+ const { parse, postprocess, preprocess } = carregar('micromark');
205
+ const { gfmTable } = carregar('micromark-extension-gfm-table');
206
+ const { decodeNamedCharacterReference } = carregar('decode-named-character-reference');
207
+ const { decodeNumericCharacterReference } = carregar('micromark-util-decode-numeric-character-reference');
208
+ if (typeof parse !== 'function' || typeof postprocess !== 'function' || typeof preprocess !== 'function' || typeof gfmTable !== 'function'
209
+ || typeof decodeNamedCharacterReference !== 'function' || typeof decodeNumericCharacterReference !== 'function') {
210
+ throw new Error('grafo.parser.indisponivel: micromark sem a API esperada');
211
+ }
212
+ const extensions = [gfmTable()];
213
+ const referencia = (valor) => {
214
+ if (valor[0] !== '#')
215
+ return decodeNamedCharacterReference(valor) || null;
216
+ const hexa = valor[1] === 'x' || valor[1] === 'X';
217
+ return decodeNumericCharacterReference(valor.slice(hexa ? 2 : 1), hexa ? 16 : 10);
218
+ };
219
+ const analisar = (texto) => postprocess(parse({ extensions }).document().write(preprocess()(texto, undefined, true)))
220
+ .map(([tipo, token]) => ({ entrada: tipo === 'enter', tipo: token.type, inicio: token.start.offset, fim: token.end.offset }));
221
+ return { analisar, referencia, versao };
222
+ }
223
+ const VARIAVEIS_DO_CJS = ['exports', 'require', 'module', '__filename', '__dirname'];
224
+ /** O V8 encerra a linha, e a linha `#!`, em LF, CR, U+2028 e U+2029. */
225
+ const FIM_DE_LINHA = new Set([0x0a, 0x0d, 0x2028, 0x2029]);
226
+ /** Processo filho: le os textos por stdin e devolve 1 (aceito), 0 (SyntaxError), 3 (pilha estourada) ou 2 (outro erro). */
227
+ const FILHO = `
228
+ const vm = require('node:vm');
229
+ const partes = [];
230
+ process.stdin.on('data', (c) => partes.push(c));
231
+ process.stdin.on('end', () => {
232
+ const textos = JSON.parse(Buffer.concat(partes).toString('utf8'));
233
+ const r = textos.map((t) => {
234
+ try { new vm.SourceTextModule(t); return 1; } catch (e) { return e instanceof SyntaxError ? 0 : e instanceof RangeError ? 3 : 2; }
235
+ });
236
+ process.stdout.write(JSON.stringify(r));
237
+ });
238
+ `;
239
+ /**
240
+ * Como o carregador do Node le a fonte: a linha `#!` do inicio em branco e, so no ESM, sem o BOM (o
241
+ * carregador CommonJS recusa BOM antes do `#!`).
242
+ */
243
+ function comoOCarregadorLe(texto, formato) {
244
+ const t = formato === 'esm' && texto.charCodeAt(0) === 0xfeff ? texto.slice(1) : texto;
245
+ if (!t.startsWith('#!'))
246
+ return t;
247
+ let fim = 2;
248
+ while (fim < t.length && !FIM_DE_LINHA.has(t.charCodeAt(fim)))
249
+ fim++;
250
+ return ' '.repeat(fim) + t.slice(fim);
251
+ }
252
+ /** KG2 D16: o juiz de sintaxe do JavaScript, em lote e com cache por texto dentro de uma extracao. */
253
+ function criarJuizDeSintaxe() {
254
+ const vistos = new Map();
255
+ const cjs = (texto) => {
256
+ try {
257
+ vm.compileFunction(texto, VARIAVEIS_DO_CJS);
258
+ return true;
259
+ }
260
+ catch (e) {
261
+ if (e instanceof SyntaxError)
262
+ return false;
263
+ throw e;
264
+ }
265
+ };
266
+ const esmEmLote = (textos) => {
267
+ const r = (0, node_child_process_1.spawnSync)(process.execPath, ['--experimental-vm-modules', '--no-warnings', '-e', FILHO], {
268
+ input: JSON.stringify(textos), encoding: 'utf8', env: {}, maxBuffer: 64 * 1024 * 1024, timeout: 600000,
269
+ });
270
+ if (r.error || r.signal || r.status !== 0) {
271
+ throw new Error(`sintaxe.esm.indisponivel: ${r.error ? r.error.code ?? 'erro' : r.signal ?? `status ${r.status}`}`);
272
+ }
273
+ let vereditos = null;
274
+ try {
275
+ vereditos = JSON.parse(r.stdout);
276
+ }
277
+ catch {
278
+ vereditos = null;
279
+ }
280
+ if (!Array.isArray(vereditos) || vereditos.length !== textos.length || vereditos.some((v) => ![0, 1, 2, 3].includes(v))) {
281
+ throw new Error('sintaxe.esm.indisponivel: resposta invalida do filho');
282
+ }
283
+ // Pilha estourada no V8 do filho: o extrator a trata como a propria (`extracao.limite.pilha`).
284
+ if (vereditos.includes(3))
285
+ throw new RangeError('Maximum call stack size exceeded no juiz de sintaxe ESM');
286
+ if (vereditos.includes(2))
287
+ throw new Error('sintaxe.esm.indisponivel: erro que nao e de sintaxe no filho');
288
+ return vereditos.map((v) => v === 1);
289
+ };
290
+ const sintaxe = (pedidos) => {
291
+ const chaves = pedidos.map((p) => p.formato + (0, node_crypto_1.createHash)('sha256').update(p.texto).digest('hex'));
292
+ const esm = new Map();
293
+ pedidos.forEach((p, i) => {
294
+ if (vistos.has(chaves[i]) || esm.has(chaves[i]))
295
+ return;
296
+ const t = comoOCarregadorLe(p.texto, p.formato);
297
+ if (p.formato === 'cjs')
298
+ vistos.set(chaves[i], cjs(t));
299
+ else
300
+ esm.set(chaves[i], t);
301
+ });
302
+ if (esm.size) {
303
+ const lista = [...esm];
304
+ const r = esmEmLote(lista.map(([, t]) => t));
305
+ lista.forEach(([k], i) => vistos.set(k, r[i]));
306
+ }
307
+ return chaves.map((k) => vistos.get(k));
308
+ };
309
+ return { sintaxe, versao: `node.${process.versions.node}` };
310
+ }
311
+ /**
312
+ * O `Parser` do extrator do KG2, com o compilador e o micromark desta instalacao. Confere que o
313
+ * compilador carregado e o da versao lida para a chave do indice.
314
+ */
315
+ function carregarAnalisadores() {
316
+ const versoes = versoesDosAnalisadores();
317
+ // CHECK rodada 3: dependencia transitiva que falta dentro da instalacao seria achada pelo Node fora
318
+ // dela (no node_modules de uma pasta acima) e carregada. Recusa antes de carregar qualquer pacote.
319
+ const ausentes = pacotesDosAnalisadores().filter((p) => p.endsWith('@ausente'));
320
+ if (ausentes.length)
321
+ throw new Error(`grafo.parser.indisponivel: dependencia fora da instalacao do ork (${ausentes.join(', ')})`);
322
+ const ts = carregar('typescript');
323
+ if (!ts || typeof ts.createProgram !== 'function' || ts.version !== versoes.typescript) {
324
+ throw new Error(`grafo.parser.indisponivel: typescript ${ts.version} carregado, ${versoes.typescript} no package.json`);
325
+ }
326
+ return { ts, unicode: versoes.unicode, markdown: carregarMarkdown(versoes.markdown), javascript: criarJuizDeSintaxe() };
327
+ }
@@ -0,0 +1,344 @@
1
+ "use strict";
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;
4
+ exports.filtrarGrafo = filtrarGrafo;
5
+ exports.prepararConsulta = prepararConsulta;
6
+ exports.resolverNo = resolverNo;
7
+ exports.vizinhos = vizinhos;
8
+ exports.chamadores = chamadores;
9
+ exports.importadores = importadores;
10
+ exports.caminho = caminho;
11
+ exports.jsonDaResposta = jsonDaResposta;
12
+ exports.textoDaResposta = textoDaResposta;
13
+ /**
14
+ * RM-031 KG3 (D5 a D7): consulta sobre o grafo do indice.
15
+ *
16
+ * Modulo puro: recebe o grafo ja lido e a concessao, sem arquivo, processo, rede, relogio nem acaso.
17
+ * So percorre arestas do grafo: nada de busca textual, inferencia ou similaridade. O que o extrator
18
+ * do KG2 nao prova nao esta no grafo, e toda resposta diz que e parcial.
19
+ *
20
+ * D6: a concessao filtra antes de qualquer mapa (`filtrarGrafo`): no, aresta, evidencia e
21
+ * diagnostico fora dela nao existem para a consulta nem para a amostra, nem em resposta, contagem,
22
+ * candidato ou erro.
23
+ *
24
+ * D7: a resposta e a mesma para o mesmo grafo e a mesma pergunta, byte a byte, em texto e em JSON
25
+ * (`ork.code-graph-query/v0`, provisorio e fora do contrato): ordem por code point, sem horario.
26
+ */
27
+ const intelligence_graph_contract_1 = require("./intelligence-graph-contract");
28
+ exports.CONSULTA_SCHEMA = 'ork.code-graph-query/v0';
29
+ exports.PROFUNDIDADE_MAXIMA = 5;
30
+ exports.LIMITE_PADRAO = 500;
31
+ exports.LIMITE_MAXIMO = 10_000;
32
+ /** Candidatos listados quando um nome solto e ambiguo. */
33
+ exports.CANDIDATOS_LISTADOS = 20;
34
+ 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';
35
+ /** Erro tipado da consulta; `candidatos` so no nome ambiguo, e so com nos visiveis. */
36
+ class ErroDeConsulta extends Error {
37
+ codigo;
38
+ detalhe;
39
+ candidatos;
40
+ constructor(codigo, detalhe, candidatos = []) {
41
+ super(`${codigo}: ${detalhe}`);
42
+ this.codigo = codigo;
43
+ this.detalhe = detalhe;
44
+ this.candidatos = candidatos;
45
+ }
46
+ }
47
+ exports.ErroDeConsulta = ErroDeConsulta;
48
+ const TIPOS_DE_NO_COM_FRAGMENTO = ['symbol', 'section', 'artifact'];
49
+ const chaveDoLocalizador = (kind, p, fragmento) => `${kind}\u0000${p}\u0000${fragmento ?? ''}`;
50
+ const rotuloDoNo = (n) => `${n.kind} ${n.locator.path}${n.locator.fragment === null ? '' : `#${n.locator.fragment}`}`;
51
+ exports.rotuloDoNo = rotuloDoNo;
52
+ function visivel(a, c) {
53
+ return a.tenant_id === c.tenant_id && a.acl_refs.every((r) => c.acl_refs.includes(r));
54
+ }
55
+ /**
56
+ * D6: o grafo so com o que a concessao ve. Aresta precisa estar visivel, com as duas pontas visiveis
57
+ * e ao menos uma evidencia visivel; evidencia e diagnostico fora da concessao saem.
58
+ */
59
+ function filtrarGrafo(grafo, concessao) {
60
+ const nodes = grafo.nodes.filter((n) => visivel(n.access, concessao));
61
+ const ids = new Set(nodes.map((n) => n.node_id));
62
+ const edges = [];
63
+ for (const a of grafo.edges) {
64
+ if (!visivel(a.access, concessao) || !ids.has(a.from) || !ids.has(a.to))
65
+ continue;
66
+ const evidence = a.evidence.filter((e) => visivel(e.access, concessao));
67
+ if (evidence.length)
68
+ edges.push(evidence.length === a.evidence.length ? a : { ...a, evidence });
69
+ }
70
+ return { ...grafo, nodes, edges, diagnostics: grafo.diagnostics.filter((d) => visivel(d.access, concessao)) };
71
+ }
72
+ /** D6: filtra pela concessao e monta os mapas. Nada fora dela entra em mapa nenhum. */
73
+ function prepararConsulta(grafoInteiro, concessao) {
74
+ const grafo = filtrarGrafo(grafoInteiro, concessao);
75
+ const nos = new Map(), rotulos = new Map(), porLocalizador = new Map();
76
+ const arquivos = new Map(), porFragmento = new Map();
77
+ for (const n of grafo.nodes) {
78
+ nos.set(n.node_id, n);
79
+ rotulos.set(n.node_id, (0, exports.rotuloDoNo)(n));
80
+ porLocalizador.set(chaveDoLocalizador(n.kind, n.locator.path, n.locator.fragment), n);
81
+ if (n.kind === 'file')
82
+ arquivos.set(n.locator.path, n);
83
+ else if (n.locator.fragment !== null) {
84
+ const lista = porFragmento.get(n.locator.fragment) ?? [];
85
+ lista.push(n);
86
+ porFragmento.set(n.locator.fragment, lista);
87
+ }
88
+ }
89
+ const saida = new Map(), entrada = new Map();
90
+ for (const aresta of grafo.edges) {
91
+ for (const [mapa, id] of [[saida, aresta.from], [entrada, aresta.to]]) {
92
+ const lista = mapa.get(id) ?? [];
93
+ lista.push(aresta);
94
+ mapa.set(id, lista);
95
+ }
96
+ }
97
+ const ordem = (outra) => (x, y) => (0, intelligence_graph_contract_1.compararUtf8)(x.kind, y.kind) || (0, intelligence_graph_contract_1.compararUtf8)(outra(x), outra(y)) || (0, intelligence_graph_contract_1.compararUtf8)(x.edge_id, y.edge_id);
98
+ for (const lista of saida.values())
99
+ lista.sort(ordem((a) => rotulos.get(a.to)));
100
+ for (const lista of entrada.values())
101
+ lista.sort(ordem((a) => rotulos.get(a.from)));
102
+ for (const lista of porFragmento.values())
103
+ 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 };
105
+ }
106
+ function ambiguo(texto, candidatos) {
107
+ const rotulos = candidatos.map(exports.rotuloDoNo).sort(intelligence_graph_contract_1.compararUtf8);
108
+ const listados = rotulos.slice(0, exports.CANDIDATOS_LISTADOS);
109
+ throw new ErroDeConsulta('grafo.consulta.ambiguo', `${texto} tem ${rotulos.length} candidatos: ${listados.join('; ')}${rotulos.length > listados.length ? '; ...' : ''}`, listados);
110
+ }
111
+ /** Os nos com o localizador `caminho#fragmento` em cada `#` do texto: o fragmento tambem pode ter `#` (`Classe.#privado`). */
112
+ function porLocalizadores(g, texto, tipos) {
113
+ const achados = new Map();
114
+ for (let i = texto.indexOf('#'); i > 0; i = texto.indexOf('#', i + 1)) {
115
+ for (const k of tipos) {
116
+ const n = g.porLocalizador.get(chaveDoLocalizador(k, texto.slice(0, i), texto.slice(i + 1)));
117
+ if (n)
118
+ achados.set(n.node_id, n);
119
+ }
120
+ }
121
+ return [...achados.values()];
122
+ }
123
+ /**
124
+ * D5: o no pelo texto. `tipo:caminho#fragmento` fixa o tipo; `caminho` exato e o arquivo;
125
+ * `caminho#fragmento` e o no com esse localizador, em qualquer `#` que separe um caminho que existe;
126
+ * o resto e nome solto, que casa o fragmento exato de um simbolo, secao ou artefato. Ambiguo lista os
127
+ * candidatos, nunca escolhe. O rotulo que a resposta imprime (sem o tipo) volta como entrada.
128
+ */
129
+ function resolverNo(g, texto) {
130
+ if (!texto)
131
+ throw new ErroDeConsulta('grafo.consulta.uso', 'no vazio');
132
+ const desconhecido = () => {
133
+ throw new ErroDeConsulta('grafo.consulta.no-desconhecido', texto);
134
+ };
135
+ const tipado = /^(file|symbol|section|artifact):(.+)$/.exec(texto);
136
+ if (tipado) {
137
+ const kind = tipado[1], resto = tipado[2];
138
+ if (kind === 'file')
139
+ return g.arquivos.get(resto) ?? desconhecido();
140
+ const candidatos = porLocalizadores(g, resto, [kind]);
141
+ if (candidatos.length === 1)
142
+ return candidatos[0];
143
+ if (candidatos.length > 1)
144
+ ambiguo(texto, candidatos);
145
+ return desconhecido();
146
+ }
147
+ const arquivo = g.arquivos.get(texto);
148
+ if (arquivo)
149
+ return arquivo;
150
+ const candidatos = porLocalizadores(g, texto, TIPOS_DE_NO_COM_FRAGMENTO);
151
+ if (candidatos.length === 1)
152
+ return candidatos[0];
153
+ if (candidatos.length > 1)
154
+ ambiguo(texto, candidatos);
155
+ const soltos = g.porFragmento.get(texto) ?? [];
156
+ if (soltos.length === 1)
157
+ return soltos[0];
158
+ if (soltos.length > 1)
159
+ ambiguo(texto, soltos);
160
+ return desconhecido();
161
+ }
162
+ function noDaResposta(g, n, distancia) {
163
+ return { rotulo: g.rotulos.get(n.node_id), kind: n.kind, path: n.locator.path, fragment: n.locator.fragment, node_id: n.node_id, distancia };
164
+ }
165
+ function arestaDaResposta(g, a, distancia) {
166
+ const evidencias = a.evidence.map((e) => ({
167
+ extractor_id: e.extractor_id, extractor_version: e.extractor_version, extraction_method: e.extraction_method, path: e.path, span: e.span,
168
+ })).sort((x, y) => (0, intelligence_graph_contract_1.compararUtf8)(x.path, y.path) || (x.span.byte_start - y.span.byte_start) || (x.span.byte_end - y.span.byte_end)
169
+ || (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
+ return { kind: a.kind, from: g.rotulos.get(a.from), to: g.rotulos.get(a.to), edge_id: a.edge_id, distancia, evidencias };
171
+ }
172
+ /** CHECK rodada 1 (A2): primeiro as arestas mais perto do alvo, e o limite corta as mais longe. */
173
+ const ordemDasArestas = (x, y) => (x.distancia - y.distancia)
174
+ || (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);
175
+ function incidentes(g, id, sentido, tipos) {
176
+ const r = [];
177
+ if (sentido !== 'entrada')
178
+ for (const a of g.saida.get(id) ?? [])
179
+ if (tipos.has(a.kind))
180
+ r.push({ aresta: a, outra: a.to, sentido: 'saida' });
181
+ if (sentido !== 'saida')
182
+ for (const a of g.entrada.get(id) ?? [])
183
+ if (tipos.has(a.kind))
184
+ r.push({ aresta: a, outra: a.from, sentido: 'entrada' });
185
+ return r;
186
+ }
187
+ function inteiroEntre(nome, v, min, max) {
188
+ if (!Number.isSafeInteger(v) || v < min || v > max)
189
+ throw new ErroDeConsulta('grafo.consulta.uso', `${nome} precisa ser inteiro de ${min} a ${max}`);
190
+ return v;
191
+ }
192
+ function tiposValidos(tipos) {
193
+ const lista = [...new Set(tipos ?? intelligence_graph_contract_1.TIPOS_DE_ARESTA)];
194
+ for (const t of lista)
195
+ if (!intelligence_graph_contract_1.TIPOS_DE_ARESTA.includes(t))
196
+ throw new ErroDeConsulta('grafo.consulta.uso', `tipo de aresta desconhecido: ${t}`);
197
+ if (!lista.length)
198
+ throw new ErroDeConsulta('grafo.consulta.uso', 'nenhum tipo de aresta');
199
+ return lista.sort(intelligence_graph_contract_1.compararUtf8);
200
+ }
201
+ /**
202
+ * D5: o raio `profundidade` em volta do alvo, pelo sentido e pelos tipos pedidos. Entram os nos a
203
+ * ate `profundidade` saltos e todas as arestas que saem dos nos a menos de `profundidade` saltos,
204
+ * cada uma com a distancia do no que a explorou. `limite` corta pela distancia e pela ordem fixa, e
205
+ * a lista de nos so traz o alvo e as pontas das arestas devolvidas.
206
+ */
207
+ function vizinhanca(g, cabecalho, tipo, texto, opcoes) {
208
+ const profundidade = inteiroEntre('profundidade', opcoes.profundidade ?? 1, 1, exports.PROFUNDIDADE_MAXIMA);
209
+ const limite = inteiroEntre('limite', opcoes.limite ?? exports.LIMITE_PADRAO, 1, exports.LIMITE_MAXIMO);
210
+ const sentido = opcoes.sentido ?? 'ambos';
211
+ if (!['entrada', 'saida', 'ambos'].includes(sentido))
212
+ throw new ErroDeConsulta('grafo.consulta.uso', `sentido desconhecido: ${sentido}`);
213
+ const tipos = tiposValidos(opcoes.tipos), permitidos = new Set(tipos);
214
+ const alvo = resolverNo(g, texto);
215
+ const distancia = new Map([[alvo.node_id, 0]]), vistas = new Map();
216
+ let camada = [alvo.node_id];
217
+ for (let d = 0; d < profundidade && camada.length; d++) {
218
+ const proxima = [];
219
+ for (const id of camada.sort((x, y) => (0, intelligence_graph_contract_1.compararUtf8)(g.rotulos.get(x), g.rotulos.get(y)))) {
220
+ for (const { aresta, outra } of incidentes(g, id, sentido, permitidos)) {
221
+ if (!vistas.has(aresta.edge_id))
222
+ vistas.set(aresta.edge_id, { aresta, d });
223
+ if (!distancia.has(outra)) {
224
+ distancia.set(outra, d + 1);
225
+ proxima.push(outra);
226
+ }
227
+ }
228
+ }
229
+ camada = proxima;
230
+ }
231
+ const todas = [...vistas.values()].map(({ aresta, d }) => arestaDaResposta(g, aresta, d)).sort(ordemDasArestas);
232
+ const devolvidas = todas.slice(0, limite);
233
+ const idPorRotulo = new Map([...distancia.keys()].map((id) => [g.rotulos.get(id), id]));
234
+ const pontas = new Set([alvo.node_id, ...devolvidas.flatMap((a) => [idPorRotulo.get(a.from), idPorRotulo.get(a.to)])]);
235
+ const nos = [...pontas].map((id) => noDaResposta(g, g.nos.get(id), distancia.get(id)))
236
+ .sort((x, y) => (x.distancia - y.distancia) || (0, intelligence_graph_contract_1.compararUtf8)(x.rotulo, y.rotulo));
237
+ return {
238
+ schema: exports.CONSULTA_SCHEMA,
239
+ consulta: { tipo, alvo: texto, para: null, profundidade, sentido, tipos, limite },
240
+ indice: cabecalho, alvo: noDaResposta(g, alvo, 0), para: null, nos, total_nos: distancia.size,
241
+ arestas: devolvidas, total_arestas: todas.length, truncado: todas.length > limite,
242
+ caminho: null, nos_explorados: null, parcial: exports.AVISO_DE_PARCIALIDADE,
243
+ };
244
+ }
245
+ function vizinhos(g, cabecalho, alvo, opcoes = {}) {
246
+ return vizinhanca(g, cabecalho, 'vizinhos', alvo, opcoes);
247
+ }
248
+ /** Quem chama o simbolo: as arestas `calls` que chegam, ate `profundidade` saltos. */
249
+ function chamadores(g, cabecalho, alvo, opcoes = {}) {
250
+ if (resolverNo(g, alvo).kind !== 'symbol')
251
+ 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'] });
253
+ }
254
+ /** Quem importa o arquivo ou o simbolo: as arestas `imports` que chegam, ate `profundidade` saltos. */
255
+ function importadores(g, cabecalho, alvo, opcoes = {}) {
256
+ const kind = resolverNo(g, alvo).kind;
257
+ if (kind !== 'file' && kind !== 'symbol')
258
+ 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'] });
260
+ }
261
+ /**
262
+ * D5: o menor caminho de `de` a `para`, pelas arestas no sentido pedido (padrao: `saida`, a direcao
263
+ * da aresta). Busca em largura com a expansao na ordem fixa: entre caminhos do mesmo tamanho, sai
264
+ * sempre o mesmo. Sem caminho, `caminho` e `null`, com os nos explorados.
265
+ */
266
+ function caminho(g, cabecalho, de, para, opcoes = {}) {
267
+ const sentido = opcoes.sentido ?? 'saida';
268
+ if (!['entrada', 'saida', 'ambos'].includes(sentido))
269
+ throw new ErroDeConsulta('grafo.consulta.uso', `sentido desconhecido: ${sentido}`);
270
+ const tipos = tiposValidos(opcoes.tipos), permitidos = new Set(tipos);
271
+ const origem = resolverNo(g, de), destino = resolverNo(g, para);
272
+ const anterior = new Map([[origem.node_id, null]]);
273
+ const fila = [origem.node_id];
274
+ for (let i = 0; i < fila.length && !anterior.has(destino.node_id); i++) {
275
+ for (const { aresta, outra, sentido: s } of incidentes(g, fila[i], sentido, permitidos)) {
276
+ if (anterior.has(outra))
277
+ continue;
278
+ anterior.set(outra, { de: fila[i], aresta, sentido: s });
279
+ fila.push(outra);
280
+ }
281
+ }
282
+ let passos = null;
283
+ let nos = [noDaResposta(g, origem, 0), noDaResposta(g, destino, null)];
284
+ if (anterior.has(destino.node_id)) {
285
+ const cadeia = [];
286
+ 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) } });
288
+ }
289
+ cadeia.reverse();
290
+ cadeia.forEach((c, n) => { c.passo.aresta.distancia = n; });
291
+ passos = cadeia.map((c) => c.passo);
292
+ nos = [origem.node_id, ...cadeia.map((c) => c.id)].map((id, d) => noDaResposta(g, g.nos.get(id), d));
293
+ }
294
+ return {
295
+ schema: exports.CONSULTA_SCHEMA,
296
+ consulta: { tipo: 'caminho', alvo: de, para, profundidade: null, sentido, tipos, limite: null },
297
+ indice: cabecalho, alvo: noDaResposta(g, origem, 0), para: noDaResposta(g, destino, passos ? passos.length : null),
298
+ 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,
301
+ };
302
+ }
303
+ function jsonDaResposta(r) {
304
+ return JSON.stringify(r, null, 2);
305
+ }
306
+ function linhasDaEvidencia(e) {
307
+ const s = e.span;
308
+ const onde = s.type === 'text'
309
+ ? `${e.path}:${s.line_start ?? '?'}${s.line_end !== null && s.line_end !== s.line_start ? `-${s.line_end}` : ''}`
310
+ : `${e.path} pagina ${s.page}`;
311
+ return ` ${e.extractor_id} ${e.extraction_method} ${onde} bytes ${s.byte_start}-${s.byte_end}`;
312
+ }
313
+ function linhasDaAresta(a, prefixo = ' ') {
314
+ return [`${prefixo}${a.kind} ${a.from} -> ${a.to}`, ...a.evidencias.map(linhasDaEvidencia)];
315
+ }
316
+ function textoDaResposta(r) {
317
+ const c = r.consulta, i = r.indice;
318
+ const linhas = [
319
+ `grafo de ${i.repository_id} na revisao ${i.revision.slice(0, 12)}, indice ${i.chave.slice(0, 16)}, snapshot ${i.snapshot_id.slice(0, 17)}`,
320
+ ];
321
+ if (i.arvore === 'modificada')
322
+ linhas.push('aviso: a arvore tem mudanca rastreada; a resposta e do HEAD, nao da arvore');
323
+ linhas.push(`extratores: ${i.extratores.map((e) => `${e.extractor_id} ${e.extractor_version}`).join(', ')}`);
324
+ if (c.tipo === 'caminho') {
325
+ const para = r.para;
326
+ if (!r.caminho) {
327
+ linhas.push(`sem caminho de ${r.alvo.rotulo} para ${para.rotulo} (sentido ${c.sentido}): ${r.nos_explorados} nos explorados`);
328
+ }
329
+ else {
330
+ linhas.push(`caminho de ${r.alvo.rotulo} para ${para.rotulo} (sentido ${c.sentido}): ${r.caminho.length} passo(s)`);
331
+ r.caminho.forEach((p, n) => linhas.push(...linhasDaAresta(p.aresta, ` ${n + 1}. ${p.sentido === 'entrada' ? '(contra a aresta) ' : ''}`)));
332
+ }
333
+ }
334
+ else {
335
+ const filtro = c.tipo === 'vizinhos' ? `, sentido ${c.sentido}, tipos ${c.tipos.length === intelligence_graph_contract_1.TIPOS_DE_ARESTA.length ? 'todos' : c.tipos.join(',')}` : '';
336
+ linhas.push(`${c.tipo} de ${r.alvo.rotulo} (profundidade ${c.profundidade}${filtro}): ${r.total_arestas} aresta(s), ${r.total_nos - 1} no(s)`);
337
+ for (const a of r.arestas)
338
+ linhas.push(...linhasDaAresta(a));
339
+ if (r.truncado)
340
+ linhas.push(`truncado: ${r.arestas.length} de ${r.total_arestas} arestas, as mais perto do alvo; use --limite`);
341
+ }
342
+ linhas.push(r.parcial);
343
+ return linhas.join('\n');
344
+ }