@orkastery/cli 0.4.3 → 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 (146) 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/README.md +11 -2
  4. package/adapters/claude-code/agents/ork-check.md +1 -1
  5. package/adapters/claude-code/agents/ork-go.md +2 -2
  6. package/adapters/claude-code/agents/ork-goal.md +1 -1
  7. package/adapters/claude-code/agents/ork-master.md +1 -1
  8. package/adapters/claude-code/agents/ork-plan.md +1 -1
  9. package/adapters/claude-code/agents/ork-ship.md +1 -1
  10. package/adapters/claude-code/commands/master.md +11 -8
  11. package/adapters/claude-code/commands/onboarding.md +3 -2
  12. package/adapters/claude-code/commands/ork.md +8 -0
  13. package/adapters/claude-code/commands/ship.md +2 -2
  14. package/adapters/codex/skills/ork/SKILL.md +12 -1
  15. package/adapters/hermes/README.md +3 -0
  16. package/adapters/hermes/bin/ork-abrir-thread.sh +17 -2
  17. package/adapters/hermes/bin/ork-brain.sh +3 -0
  18. package/adapters/hermes/bin/ork-maestro.sh +4 -1
  19. package/adapters/hermes/bin/ork-network-roadmap.sh +11 -0
  20. package/adapters/hermes/bin/ork-roadmap-status.sh +9 -0
  21. package/adapters/hermes/hermes.plugin.json +2 -0
  22. package/adapters/hermes/hitl-ingress/__init__.py +36 -3
  23. package/adapters/hermes/skills/orkastery-devmaster/SKILL.md +19 -20
  24. package/adapters/openclaw/README.md +45 -2
  25. package/adapters/openclaw/dist/hitl-ingress.js +37 -3
  26. package/adapters/openclaw/dist/index.js +93 -21
  27. package/adapters/openclaw/openclaw.plugin.json +10 -1
  28. package/adapters/openclaw/package.json +2 -2
  29. package/adapters/openclaw/src/hitl-ingress.ts +32 -3
  30. package/adapters/openclaw/src/index.ts +109 -21
  31. package/assets/docs/padroes/documentacao-de-produto.md +2 -2
  32. package/assets/docs/padroes/roadmap-de-produto.md +1 -1
  33. package/assets/docs/produto/_modelo-feature.md +2 -0
  34. package/assets/orkmind_bridge.py +214 -1
  35. package/dist/adapters/claude-bg.js +24 -7
  36. package/dist/auditrun.js +1 -1
  37. package/dist/board.js +74 -0
  38. package/dist/branch-de-estado.js +5 -4
  39. package/dist/busca-semantica.js +161 -0
  40. package/dist/ci.js +141 -10
  41. package/dist/claim-lint.js +3 -1
  42. package/dist/company-brain-cli.js +27 -2
  43. package/dist/company-brain-context.js +132 -0
  44. package/dist/company-brain-dossie.js +376 -0
  45. package/dist/company-brain-mcp.js +4 -1
  46. package/dist/conducao.js +71 -18
  47. package/dist/decisao-autonoma.js +42 -0
  48. package/dist/docs.js +135 -25
  49. package/dist/doctor.js +242 -32
  50. package/dist/entrega-pr.js +116 -5
  51. package/dist/experiencia-instalacao.js +256 -0
  52. package/dist/experiencia.js +72 -0
  53. package/dist/fabrica-estado.js +63 -12
  54. package/dist/fechamento.js +105 -0
  55. package/dist/fix.js +3 -3
  56. package/dist/forja.js +435 -0
  57. package/dist/gates.js +9 -1
  58. package/dist/hitl-classificacao.js +11 -1
  59. package/dist/hitl-contract.js +50 -7
  60. package/dist/hitl-curto.js +158 -0
  61. package/dist/hitl-gates.js +67 -1
  62. package/dist/hitl-lote.js +30 -15
  63. package/dist/hitl-presentation.js +10 -7
  64. package/dist/hitl-resumo.js +57 -13
  65. package/dist/hitl-texto-livre.js +150 -0
  66. package/dist/hosts.js +92 -3
  67. package/dist/index.js +637 -45
  68. package/dist/indice-vetorial.js +317 -0
  69. package/dist/init.js +24 -5
  70. package/dist/integracoes-locais.js +3 -1
  71. package/dist/intelligence-benchmark-contract.js +570 -0
  72. package/dist/intelligence-graph-cli.js +584 -0
  73. package/dist/intelligence-graph-contract.js +636 -0
  74. package/dist/intelligence-graph-extract-md.js +487 -0
  75. package/dist/intelligence-graph-extract-ts.js +980 -0
  76. package/dist/intelligence-graph-extract.js +546 -0
  77. package/dist/intelligence-graph-index.js +693 -0
  78. package/dist/intelligence-graph-parsers.js +327 -0
  79. package/dist/intelligence-graph-query.js +344 -0
  80. package/dist/intelligence-graph-repo.js +197 -0
  81. package/dist/leases.js +100 -0
  82. package/dist/ledger.js +23 -0
  83. package/dist/licoes.js +5 -4
  84. package/dist/liveness.js +2 -1
  85. package/dist/maestro-cli.js +10 -1
  86. package/dist/maestro-contract.js +5 -1
  87. package/dist/maestro-discovery.js +16 -2
  88. package/dist/maestro-runtime.js +3 -0
  89. package/dist/maestro-snapshot.js +6 -2
  90. package/dist/manifest.js +166 -5
  91. package/dist/master-batch.js +3 -2
  92. package/dist/master-digest.js +2 -2
  93. package/dist/master-nota.js +203 -0
  94. package/dist/master.js +4 -0
  95. package/dist/mcp-artifacts.js +2 -0
  96. package/dist/mcp-experiencia.js +164 -0
  97. package/dist/mcp-git.js +64 -12
  98. package/dist/mcp-grafo-worker.js +91 -0
  99. package/dist/mcp-grafo.js +233 -0
  100. package/dist/mcp-install.js +5 -2
  101. package/dist/mcp-maestro.js +3 -2
  102. package/dist/mcp-server.js +93 -5
  103. package/dist/mcp-ship.js +2 -0
  104. package/dist/mcp-verify.js +3 -1
  105. package/dist/memoria.js +147 -1
  106. package/dist/network-roadmap.js +907 -0
  107. package/dist/ocupacao.js +43 -3
  108. package/dist/onboarding.js +122 -11
  109. package/dist/orkmind.js +165 -9
  110. package/dist/orquestracao.js +2 -1
  111. package/dist/parado-no-condutor.js +933 -0
  112. package/dist/phase.js +166 -13
  113. package/dist/policies.js +29 -2
  114. package/dist/projeto-alvo.js +551 -0
  115. package/dist/prompts.js +1 -1
  116. package/dist/prova-ativacao.js +345 -0
  117. package/dist/pulse-consentimento.js +4 -2
  118. package/dist/pulse-delivery.js +32 -6
  119. package/dist/pulse-resposta.js +674 -49
  120. package/dist/pulse.js +74 -3
  121. package/dist/retry.js +51 -8
  122. package/dist/roadmap-reservas.js +262 -20
  123. package/dist/roadmap-status.js +348 -0
  124. package/dist/runtime-context.js +5 -1
  125. package/dist/sessoes-inventario.js +7 -1
  126. package/dist/sessoes.js +4 -1
  127. package/dist/ship.js +25 -1
  128. package/dist/thread-close.js +3 -0
  129. package/dist/thread.js +21 -2
  130. package/dist/util.js +71 -4
  131. package/dist/verify.js +6 -5
  132. package/dist/worktree.js +29 -6
  133. package/eval/casos/onboarding.json +1 -1
  134. package/eval/casos/orchestration-experience-pt-br.json +48 -0
  135. package/eval/casos/orchestration-experience.json +48 -0
  136. package/package.json +1 -1
  137. package/schemas/code-artifact-graph.v1.schema.json +581 -0
  138. package/schemas/graph-benchmark.v1.schema.json +2240 -0
  139. package/schemas/maestro-snapshot.schema.json +23 -0
  140. package/skills/README.md +7 -5
  141. package/skills/core/onboarding/SKILL.md +10 -2
  142. package/skills/core/orchestration-experience/SKILL.md +113 -0
  143. package/skills/core/orchestration-experience-pt-br/SKILL.md +112 -0
  144. package/skills/core/orkastery-bootstrap/SKILL.md +2 -2
  145. package/skills/phases/go-implementation/SKILL.md +2 -0
  146. package/skills/phases/ship-release/SKILL.md +6 -3
@@ -0,0 +1,584 @@
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.CORRECAO_DO_INDICE = exports.USO_DO_GRAFO = exports.AMOSTRA_SCHEMA = exports.STATUS_SCHEMA = void 0;
37
+ exports.lerPedido = lerPedido;
38
+ exports.executarGrafo = executarGrafo;
39
+ /**
40
+ * RM-031 KG3 (D7, D8): `ork grafo`, o CLI do indice e da consulta do grafo de codigo.
41
+ *
42
+ * Substitui o comando provisorio do KG2: `indexar --verificar` prova a extracao (contrato, bytes e
43
+ * determinismo com a ordem de leitura trocada), e `amostra` gera e confere a amostra auditada de
44
+ * arestas. As consultas leem o indice do HEAD e respondem em texto ou `--json`.
45
+ *
46
+ * O argv chega cru e o parser e estrito: subcomando conhecido, opcao conhecida uma vez so, valor
47
+ * onde a opcao pede valor e bandeira sem valor, numero exato de posicionais. Resposta sai 0, mesmo
48
+ * vazia; erro tipado sai 1 e, com `--json`, vem como objeto.
49
+ *
50
+ * RM-031 KG5 (D4, D5): `--teto-bytes N` limita a resposta em bytes (JSON compacto), para quem a leva
51
+ * ao contexto de um agente, como as tools `ork_grafo_*` do MCP; sem a opcao, a saida e a de antes. Sem
52
+ * o indice do HEAD, a recusa diz se ha indice de outra revisao ou de outro extrator, e a correcao.
53
+ */
54
+ const node_crypto_1 = require("node:crypto");
55
+ const fs = __importStar(require("node:fs"));
56
+ const path = __importStar(require("node:path"));
57
+ const intelligence_graph_contract_1 = require("./intelligence-graph-contract");
58
+ const intelligence_graph_index_1 = require("./intelligence-graph-index");
59
+ const intelligence_graph_query_1 = require("./intelligence-graph-query");
60
+ const intelligence_graph_repo_1 = require("./intelligence-graph-repo");
61
+ exports.STATUS_SCHEMA = 'ork.code-graph-index-status/v0';
62
+ /** O mesmo schema da amostra do KG2: as amostras auditadas continuam validas. */
63
+ exports.AMOSTRA_SCHEMA = 'ork.graph-edge-audit-sample/v0';
64
+ exports.USO_DO_GRAFO = [
65
+ 'uso: ork grafo indexar [--verificar] [--forcar] [--json]',
66
+ ' ork grafo status [--json]',
67
+ ' ork grafo vizinhos <no> [--profundidade N] [--sentido entrada|saida|ambos] [--tipo T,...] [--limite N] [--json [--teto-bytes N]]',
68
+ ' ork grafo chamadores <simbolo> [--profundidade N] [--limite N] [--json [--teto-bytes N]]',
69
+ ' ork grafo importadores <arquivo|simbolo> [--profundidade N] [--limite N] [--json [--teto-bytes N]]',
70
+ ' ork grafo caminho <de> <para> [--sentido saida|entrada|ambos] [--tipo T,...] [--json [--teto-bytes N]]',
71
+ ' ork grafo amostra [--por-estrato N] | ork grafo amostra --conferir ARQ [--json]',
72
+ ' ork grafo limpar [--tudo] [--json]',
73
+ ].join('\n');
74
+ /** KG5 (D5): a correcao das recusas de indice que a proxima indexacao resolve. */
75
+ exports.CORRECAO_DO_INDICE = 'ork grafo indexar';
76
+ /** KG5 (D5): o estado do indice do HEAD que cada recusa declara no JSON, ao lado da correcao. */
77
+ const ESTADO_DO_INDICE = Object.freeze({
78
+ 'grafo.indice.ausente': 'nao-indexado',
79
+ 'grafo.indice.outra-revisao': 'outra-revisao',
80
+ 'grafo.indice.outro-extrator': 'outro-extrator',
81
+ 'grafo.indice.corrompido': 'corrompido',
82
+ });
83
+ const SUBCOMANDOS = Object.freeze({
84
+ indexar: { posicionais: 0, bandeiras: ['verificar', 'forcar', 'json'], valores: [] },
85
+ status: { posicionais: 0, bandeiras: ['json'], valores: [] },
86
+ vizinhos: { posicionais: 1, bandeiras: ['json'], valores: ['profundidade', 'sentido', 'tipo', 'limite', 'teto-bytes'] },
87
+ chamadores: { posicionais: 1, bandeiras: ['json'], valores: ['profundidade', 'limite', 'teto-bytes'] },
88
+ importadores: { posicionais: 1, bandeiras: ['json'], valores: ['profundidade', 'limite', 'teto-bytes'] },
89
+ caminho: { posicionais: 2, bandeiras: ['json'], valores: ['sentido', 'tipo', 'teto-bytes'] },
90
+ amostra: { posicionais: 0, bandeiras: ['json'], valores: ['por-estrato', 'conferir'] },
91
+ limpar: { posicionais: 0, bandeiras: ['tudo', 'json'], valores: [] },
92
+ });
93
+ function uso(motivo) {
94
+ throw new Error(`grafo.uso: ${motivo}\n${exports.USO_DO_GRAFO}`);
95
+ }
96
+ function lerPedido(argv) {
97
+ const [sub, ...resto] = argv;
98
+ if (!sub)
99
+ uso('falta o subcomando');
100
+ const esp = SUBCOMANDOS[sub];
101
+ if (!Object.prototype.hasOwnProperty.call(SUBCOMANDOS, sub) || !esp)
102
+ uso(`subcomando desconhecido: ${sub}`);
103
+ const posicionais = [], bandeiras = new Set(), valores = new Map();
104
+ for (let i = 0; i < resto.length; i++) {
105
+ const a = resto[i];
106
+ if (!a.startsWith('--')) {
107
+ posicionais.push(a);
108
+ continue;
109
+ }
110
+ const igual = a.indexOf('=');
111
+ const nome = igual > 0 ? a.slice(2, igual) : a.slice(2);
112
+ if (bandeiras.has(nome) || valores.has(nome))
113
+ uso(`--${nome} repetida`);
114
+ if (esp.bandeiras.includes(nome)) {
115
+ if (igual > 0)
116
+ uso(`--${nome} nao leva valor`);
117
+ bandeiras.add(nome);
118
+ }
119
+ else if (esp.valores.includes(nome)) {
120
+ const valor = igual > 0 ? a.slice(igual + 1) : resto[++i];
121
+ if (valor === undefined || valor === '' || (igual < 0 && valor.startsWith('--')))
122
+ uso(`--${nome} exige valor`);
123
+ valores.set(nome, valor);
124
+ }
125
+ else {
126
+ uso(`opcao desconhecida para ${sub}: ${a}`);
127
+ }
128
+ }
129
+ if (posicionais.length !== esp.posicionais)
130
+ uso(`${sub} pede ${esp.posicionais} argumento(s), veio ${posicionais.length}`);
131
+ return { sub, posicionais, bandeiras, valores };
132
+ }
133
+ function inteiro(p, nome) {
134
+ const v = p.valores.get(nome);
135
+ if (v === undefined)
136
+ return undefined;
137
+ if (!/^[0-9]{1,6}$/.test(v))
138
+ uso(`--${nome} precisa ser inteiro`);
139
+ return Number(v);
140
+ }
141
+ function tipos(p) {
142
+ const v = p.valores.get('tipo');
143
+ if (v === undefined)
144
+ return undefined;
145
+ const lista = v.split(',');
146
+ for (const t of lista)
147
+ if (!intelligence_graph_contract_1.TIPOS_DE_ARESTA.includes(t))
148
+ uso(`--tipo desconhecido: ${t} (use ${intelligence_graph_contract_1.TIPOS_DE_ARESTA.join(',')})`);
149
+ return lista;
150
+ }
151
+ function sentido(p) {
152
+ const v = p.valores.get('sentido');
153
+ if (v !== undefined && !['entrada', 'saida', 'ambos'].includes(v))
154
+ uso(`--sentido desconhecido: ${v}`);
155
+ return v;
156
+ }
157
+ /** KG5 (D4): o teto vale para o JSON, que e o que vai ao contexto de quem consulta. */
158
+ function tetoDeBytes(p) {
159
+ const teto = inteiro(p, 'teto-bytes');
160
+ if (teto === undefined)
161
+ return undefined;
162
+ if (!p.bandeiras.has('json'))
163
+ uso('--teto-bytes exige --json');
164
+ if (teto < 1)
165
+ uso('--teto-bytes precisa ser ao menos 1');
166
+ return teto;
167
+ }
168
+ const mb = (bytes) => `${(bytes / 1024 / 1024).toFixed(1)} MB`;
169
+ const contagem = (o) => Object.entries(o).map(([k, v]) => `${k}=${v}`).join(' ');
170
+ /** Ate cinco caminhos e quantos ficaram de fora. */
171
+ const alguns = (lista) => (lista.length ? ` (${lista.slice(0, 5).join(', ')}${lista.length > 5 ? ` e mais ${lista.length - 5}` : ''})` : '');
172
+ /** KG4: como a extracao rodou, a base e o que veio dela, ou por que foi completa. */
173
+ function linhasDaExtracao(r) {
174
+ if (r.modo === null)
175
+ return [];
176
+ const a = r.reaproveitamento;
177
+ if (r.modo === 'incremental' && r.base && a) {
178
+ // Desestruturado: o lint de horario le o campo de nome curto do TypeScript como instante.
179
+ const { ts: doTs, md: doMd, mudanca } = a;
180
+ const ts = doTs.modo === 'inteiro' ? `TypeScript inteiro (${doTs.motivo})`
181
+ : doTs.modo === 'parcial' ? `TypeScript parcial, ${doTs.reextraidos.length} reextraido(s)${alguns(doTs.reextraidos)} num programa de ${doTs.programa}, ${doTs.reaproveitados} da base`
182
+ : `TypeScript todo da base (${doTs.reaproveitados})`;
183
+ return [
184
+ ` extracao incremental a partir do indice de ${r.base.revision.slice(0, 12)}: ${mudanca.alterados} alterado(s), ${mudanca.novos} novo(s), ${mudanca.removidos} removido(s)`,
185
+ ` ${ts}`,
186
+ ` Markdown: ${doMd.reextraidos.length} reextraido(s)${alguns(doMd.reextraidos)}, ${doMd.reaproveitados} da base`,
187
+ ];
188
+ }
189
+ return [` extracao completa${r.motivo_completo ? `: ${r.motivo_completo}` : ''}`];
190
+ }
191
+ function indexar(ctx, p) {
192
+ const r = (0, intelligence_graph_index_1.construirIndice)(ctx, { verificar: p.bandeiras.has('verificar'), forcar: p.bandeiras.has('forcar') });
193
+ if (p.bandeiras.has('json')) {
194
+ ctx.escrever(JSON.stringify(r, null, 2));
195
+ return 0;
196
+ }
197
+ const m = r.manifesto;
198
+ const linhas = [
199
+ `indice do grafo de ${m.repository_id}: ${r.estado}`,
200
+ ` revisao ${m.revision}`,
201
+ ` chave ${r.chave}`,
202
+ ` snapshot ${m.snapshot_id}`,
203
+ ` digest ${m.graph_digest}`,
204
+ ` conferencia conferirFontes verificada: ${m.conferencia.fontes} fontes e ${m.conferencia.evidencias} evidencias contra os bytes`,
205
+ ` nos ${contagem(m.contagens.nos)}`,
206
+ ` arestas ${contagem(m.contagens.arestas)}`,
207
+ ` lugar ${r.dir} (${mb(m.graph_bytes + m.report_bytes + m.units_bytes)})`,
208
+ ...linhasDaExtracao(r),
209
+ ];
210
+ if (r.motivo)
211
+ linhas.push(` motivo ${r.motivo}`);
212
+ for (const d of r.determinismo ?? []) {
213
+ linhas.push(` determinismo ${d.ordem}: digest ${d.digest.slice(0, 16)}, relatorio ${d.relatorio.slice(0, 16)}, unidades ${d.unidades.slice(0, 16)}, ${d.igual ? 'igual' : 'DIFERENTE'}`);
214
+ }
215
+ if (r.incremental)
216
+ linhas.push(` incremental a partir do indice de ${r.incremental.base.slice(0, 12)}: ${r.incremental.igual ? 'igual a completa nos quatro arquivos' : 'DIFERENTE'}`);
217
+ else if (r.determinismo)
218
+ linhas.push(` incremental nao conferido: ${r.motivo_completo ?? 'sem base'}`);
219
+ linhas.push(` tempo ${r.ms} ms`);
220
+ if (r.determinismo)
221
+ linhas.push('aprovado');
222
+ ctx.escrever(linhas.join('\n'));
223
+ return 0;
224
+ }
225
+ function status(ctx, p) {
226
+ const arvore = (0, intelligence_graph_repo_1.revisaoDaArvore)(ctx.raiz);
227
+ let chave = null, analisadores = null, erro = null;
228
+ try {
229
+ const perfil = (0, intelligence_graph_index_1.perfilDoIndice)(arvore.raiz, undefined, ctx.repositorio ? { repository_id: ctx.repositorio } : {});
230
+ analisadores = perfil.analisadores;
231
+ if (arvore.head)
232
+ chave = (0, intelligence_graph_index_1.chaveDoIndice)(arvore.head, perfil);
233
+ }
234
+ catch (e) {
235
+ erro = e.message;
236
+ }
237
+ const estado = (0, intelligence_graph_index_1.estadoDosIndices)(ctx);
238
+ const doHead = chave ? estado.indices.find((i) => i.chave === chave) ?? null : null;
239
+ // Sem analisadores nao ha chave: o indice do HEAD fica indisponivel, nunca "ausente" (indexar tambem nao roda).
240
+ const indiceDoHeadEstado = erro ? 'indisponivel' : doHead ? (doHead.problema ? 'com-problema' : 'presente') : 'ausente';
241
+ const r = {
242
+ schema: exports.STATUS_SCHEMA, head: arvore.head, arvore: arvore.motivo === null ? 'limpa' : arvore.motivo, chave_do_head: chave,
243
+ indice_do_head: indiceDoHeadEstado, analisadores, erro,
244
+ dir: estado.dir, bytes: estado.bytes, indices: estado.indices, sobras: estado.sobras,
245
+ };
246
+ if (p.bandeiras.has('json')) {
247
+ ctx.escrever(JSON.stringify(r, null, 2));
248
+ return 0;
249
+ }
250
+ const linhas = [
251
+ `grafo: HEAD ${arvore.head ? arvore.head.slice(0, 12) : 'sem commit'}, arvore ${r.arvore}`,
252
+ ` indice do HEAD ${chave ?? 'indisponivel'} ${r.indice_do_head === 'ausente' ? '(ausente: rode ork grafo indexar)'
253
+ : r.indice_do_head === 'indisponivel' ? '(sem os analisadores nesta instalacao, nem a consulta nem o indexar rodam)' : `(${r.indice_do_head})`}`,
254
+ ` analisadores ${erro ?? Object.entries(analisadores).map(([k, v]) => `${k} ${v}`).join(', ')}`,
255
+ ` pasta ${estado.dir ?? 'ainda nao criada'} (${estado.indices.length} indice(s), ${mb(estado.bytes)})`,
256
+ ];
257
+ for (const i of estado.indices) {
258
+ linhas.push(` ${i.chave.slice(0, 20)} ${i.revision ? i.revision.slice(0, 12) : '?'} ${i.repository_id ?? '?'} ${mb(i.bytes)} ${i.problema ?? 'ok'}`);
259
+ }
260
+ if (estado.sobras.length)
261
+ linhas.push(` sobras ${estado.sobras.join(', ')}`);
262
+ ctx.escrever(linhas.join('\n'));
263
+ return 0;
264
+ }
265
+ /**
266
+ * KG5 (CHECK rodada 1, A1): o que difere entre o extrator do indice guardado e o de quem consulta. A
267
+ * versao do Node entra no rotulo do analisador de JavaScript, entao outra instalacao ou outro Node dao
268
+ * outra chave com o mesmo conteudo.
269
+ */
270
+ function diferencasDoExtrator(guardado, atual) {
271
+ const r = [];
272
+ for (const k of Object.keys(atual.analisadores).sort(intelligence_graph_contract_1.compararUtf8)) {
273
+ const antes = guardado.analisadores?.[k];
274
+ if (antes !== atual.analisadores[k])
275
+ r.push(`${k} ${antes ?? '?'} no indice, ${atual.analisadores[k]} aqui`);
276
+ }
277
+ if ((0, intelligence_graph_contract_1.canonico)(guardado.pacotes) !== (0, intelligence_graph_contract_1.canonico)(atual.pacotes))
278
+ r.push('pacotes dos analisadores');
279
+ if (guardado.codigo !== atual.codigo)
280
+ r.push('codigo do extrator');
281
+ return r;
282
+ }
283
+ /**
284
+ * KG5 (D5): sem o indice do HEAD, diz o que ha no lugar. Indice guardado do mesmo repositorio e da
285
+ * revisao do HEAD com outra chave e de outro extrator (instalacao, Node, analisadores ou codigo do
286
+ * extrator mudaram), e a recusa diz o que mudou; de outra revisao, e indice velho ou de outra arvore.
287
+ * Nos dois casos a correcao e indexar de novo, com a instalacao de quem consulta; sem nenhum, fica a
288
+ * recusa original (nao indexado). Nunca responde por um indice que nao e o do HEAD.
289
+ */
290
+ function semIndiceDoHead(ctx, original) {
291
+ const arvore = (0, intelligence_graph_repo_1.revisaoDaArvore)(ctx.raiz);
292
+ if (!arvore.head)
293
+ throw original;
294
+ const perfil = (0, intelligence_graph_index_1.perfilDoIndice)(arvore.raiz, undefined, ctx.repositorio ? { repository_id: ctx.repositorio } : {});
295
+ const chave = (0, intelligence_graph_index_1.chaveDoIndice)(arvore.head, perfil), head = arvore.head.slice(0, 12);
296
+ const guardados = (0, intelligence_graph_index_1.estadoDosIndices)(ctx).indices
297
+ .filter((i) => i.problema === null && i.chave !== chave && i.repository_id === perfil.repository_id && i.revision !== null);
298
+ const doHead = guardados.find((i) => i.revision === arvore.head);
299
+ if (doHead) {
300
+ const diferencas = diferencasDoExtrator((0, intelligence_graph_index_1.lerIndice)(ctx, doHead.chave, { grafo: false }).manifesto, perfil);
301
+ throw new Error(`grafo.indice.outro-extrator: ha indice do HEAD ${head} de outro extrator (${diferencas.join('; ') || 'perfil diferente'}); `
302
+ + `rode ${exports.CORRECAO_DO_INDICE} com a mesma instalacao do ork e o mesmo Node de quem consulta (o servidor MCP consulta com a dele)`);
303
+ }
304
+ if (guardados.length) {
305
+ const revisoes = [...new Set(guardados.map((i) => i.revision.slice(0, 12)))].sort(intelligence_graph_contract_1.compararUtf8);
306
+ // CHECK rodada 2 (N7): a ressalva da instalacao ja na primeira recusa, a de cada commit do GO.
307
+ throw new Error(`grafo.indice.outra-revisao: o HEAD ${head} nao tem indice; o guardado e de outra revisao${alguns(revisoes)}; `
308
+ + `rode ${exports.CORRECAO_DO_INDICE} com a mesma instalacao do ork e o mesmo Node de quem consulta`);
309
+ }
310
+ throw original;
311
+ }
312
+ /** O indice do HEAD pronto para consulta, com a concessao local e o cabecalho; `grafo` ja vem filtrado por ela. */
313
+ function consultavel(ctx) {
314
+ let doHead;
315
+ try {
316
+ doHead = (0, intelligence_graph_index_1.indiceDoHead)(ctx);
317
+ }
318
+ catch (e) {
319
+ if (String(e.message).startsWith('grafo.indice.ausente'))
320
+ semIndiceDoHead(ctx, e);
321
+ throw e;
322
+ }
323
+ const { arvore, perfil, chave, indice } = doHead;
324
+ const concessao = (0, intelligence_graph_index_1.concessaoLocal)(arvore.raiz, perfil);
325
+ const grafo = (0, intelligence_graph_query_1.filtrarGrafo)(indice.grafo, concessao), m = indice.manifesto;
326
+ const cabecalho = {
327
+ repository_id: m.repository_id, revision: m.revision, chave, snapshot_id: m.snapshot_id, graph_digest: m.graph_digest,
328
+ arvore: arvore.motivo === null ? 'limpa' : 'modificada',
329
+ extratores: grafo.snapshot.extractors.map((e) => ({ extractor_id: e.extractor_id, extractor_version: e.extractor_version })),
330
+ };
331
+ return { g: (0, intelligence_graph_query_1.prepararConsulta)(grafo, concessao), cabecalho, grafo, raiz: arvore.raiz };
332
+ }
333
+ function responder(ctx, p, r) {
334
+ ctx.escrever(p.bandeiras.has('json') ? (0, intelligence_graph_query_1.jsonDaResposta)(r) : (0, intelligence_graph_query_1.textoDaResposta)(r));
335
+ return 0;
336
+ }
337
+ /**
338
+ * KG5 (D4): a resposta em JSON compacto que cabe em `teto` bytes. Se a pedida nao cabe, vale o maior
339
+ * limite que cabe: as arestas mais longe do alvo saem primeiro, pela mesma ordem do `--limite`, e
340
+ * `consulta.limite` passa a ser o efetivo. O tamanho cresce com o limite (cada aresta a mais so soma
341
+ * bytes), entao a busca binaria acha o maior que cabe, sempre o mesmo. Aresta nunca sai sem toda a
342
+ * evidencia; o caminho nao se corta.
343
+ */
344
+ function respostaComTeto(perguntar, limite, teto) {
345
+ const pedida = perguntar(limite);
346
+ const limitePedido = pedida.consulta.limite;
347
+ const comTeto = (r, cortado) => JSON.stringify({ ...r, teto: { bytes: teto, limite_pedido: limitePedido, cortado } });
348
+ const inteira = comTeto(pedida, false);
349
+ if (Buffer.byteLength(inteira) <= teto)
350
+ return inteira;
351
+ const ehCaminho = pedida.consulta.tipo === 'caminho';
352
+ // So chega aqui o que nao cabe nem com a aresta mais perto do alvo: estreitar a consulta nao ajuda.
353
+ const excedido = (resposta, bytes) => {
354
+ throw new intelligence_graph_query_1.ErroDeConsulta('grafo.consulta.teto-excedido', `${resposta} tem ${bytes} bytes, acima do teto de ${teto}; ${ehCaminho ? 'o caminho nao se corta: ' : ''}use --teto-bytes maior`);
355
+ };
356
+ if (ehCaminho)
357
+ excedido(`o caminho de ${pedida.arestas.length} passo(s)`, Buffer.byteLength(inteira));
358
+ if (pedida.arestas.length <= 1)
359
+ excedido(`a resposta com ${pedida.arestas.length} aresta(s)`, Buffer.byteLength(inteira));
360
+ let menor = 1, maior = pedida.arestas.length - 1, melhor = null;
361
+ while (menor <= maior) {
362
+ const k = Math.floor((menor + maior) / 2), texto = comTeto(perguntar(k), true);
363
+ if (Buffer.byteLength(texto) <= teto) {
364
+ melhor = texto;
365
+ menor = k + 1;
366
+ }
367
+ else
368
+ maior = k - 1;
369
+ }
370
+ return melhor ?? excedido('a resposta com 1 aresta', Buffer.byteLength(comTeto(perguntar(1), true)));
371
+ }
372
+ function consultar(ctx, p) {
373
+ // As opcoes sao conferidas antes de carregar o indice: erro de uso nao custa a leitura do grafo.
374
+ const [a, b] = p.posicionais;
375
+ const profundidade = inteiro(p, 'profundidade'), limite = inteiro(p, 'limite'), s = sentido(p), t = tipos(p), teto = tetoDeBytes(p);
376
+ const { g, cabecalho } = consultavel(ctx);
377
+ const perguntar = (lim) => {
378
+ if (p.sub === 'vizinhos')
379
+ return (0, intelligence_graph_query_1.vizinhos)(g, cabecalho, a, { profundidade, limite: lim, sentido: s, tipos: t });
380
+ if (p.sub === 'chamadores')
381
+ return (0, intelligence_graph_query_1.chamadores)(g, cabecalho, a, { profundidade, limite: lim });
382
+ if (p.sub === 'importadores')
383
+ return (0, intelligence_graph_query_1.importadores)(g, cabecalho, a, { profundidade, limite: lim });
384
+ return (0, intelligence_graph_query_1.caminho)(g, cabecalho, a, b, { sentido: s, tipos: t });
385
+ };
386
+ if (teto === undefined)
387
+ return responder(ctx, p, perguntar(limite));
388
+ ctx.escrever(respostaComTeto(perguntar, limite, teto));
389
+ return 0;
390
+ }
391
+ const sha256 = (b) => (0, node_crypto_1.createHash)('sha256').update(b).digest('hex');
392
+ /**
393
+ * Bytes da arvore, conferidos arquivo a arquivo contra o manifesto do indice: o trecho da amostra e
394
+ * sempre o do HEAD, mesmo com outro arquivo modificado. So le os arquivos pedidos, e so caminhos do
395
+ * manifesto; arquivo que mudou recusa a amostra.
396
+ */
397
+ function leitorDaArvore(raiz, grafo) {
398
+ const hashes = new Map(grafo.snapshot.source_manifest.map((m) => [m.path, m.source_hash]));
399
+ const lidos = new Map();
400
+ return (p) => {
401
+ const guardado = lidos.get(p);
402
+ if (guardado)
403
+ return guardado;
404
+ const esperado = hashes.get(p);
405
+ if (!esperado)
406
+ throw new Error(`grafo.amostra.fonte-desconhecida: ${p}`);
407
+ // Sem seguir link e so arquivo regular: FIFO ou dispositivo no lugar da fonte nao trava a leitura.
408
+ let fd;
409
+ try {
410
+ fd = fs.openSync(path.join(raiz, p), fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW | fs.constants.O_NONBLOCK);
411
+ }
412
+ catch {
413
+ throw new Error(`grafo.amostra.fonte-mudou: ${p}`);
414
+ }
415
+ let bytes;
416
+ try {
417
+ if (!fs.fstatSync(fd).isFile())
418
+ throw new Error(`grafo.amostra.fonte-mudou: ${p}`);
419
+ bytes = fs.readFileSync(fd);
420
+ }
421
+ finally {
422
+ fs.closeSync(fd);
423
+ }
424
+ if (sha256(bytes) !== esperado)
425
+ throw new Error(`grafo.amostra.fonte-mudou: ${p}`);
426
+ lidos.set(p, bytes);
427
+ return bytes;
428
+ };
429
+ }
430
+ const localizadorDe = (g) => new Map(g.nodes.map((n) => [n.node_id, { kind: n.kind, path: n.locator.path, fragment: n.locator.fragment }]));
431
+ const rotulo = (l) => `${l.kind}:${l.path}${l.fragment === null ? '' : `#${l.fragment}`}`;
432
+ const chaveDaAresta = (kind, de, para) => `${kind} ${rotulo(de)} -> ${rotulo(para)}`;
433
+ const trecho = (ler, e) => ler(e.path).subarray(e.span.byte_start, e.span.byte_end);
434
+ /** KG2 D12: estrato por tipo de aresta e metodos; passo fixo sobre os `edge_id` ordenados. */
435
+ function amostrar(grafo, ler, porEstrato, digest) {
436
+ const nos = localizadorDe(grafo), estratos = new Map();
437
+ for (const a of grafo.edges) {
438
+ const chave = `${a.kind}/${[...new Set(a.evidence.map((e) => e.extraction_method))].sort().join('+')}`;
439
+ if (!estratos.has(chave))
440
+ estratos.set(chave, []);
441
+ estratos.get(chave).push(a);
442
+ }
443
+ const arestas = [], universo = {};
444
+ for (const chave of [...estratos.keys()].sort()) {
445
+ const lista = estratos.get(chave).sort((x, y) => (x.edge_id < y.edge_id ? -1 : 1));
446
+ universo[chave] = lista.length;
447
+ const k = Math.min(porEstrato, lista.length);
448
+ for (let i = 0; i < k; i++) {
449
+ const a = lista[Math.floor((i * lista.length) / k)];
450
+ const e = [...a.evidence].sort((x, y) => (x.path === y.path ? x.span.byte_start - y.span.byte_start : x.path < y.path ? -1 : 1))[0];
451
+ const t = trecho(ler, e);
452
+ const span = e.span;
453
+ arestas.push({
454
+ estrato: chave, kind: a.kind, from: nos.get(a.from), to: nos.get(a.to),
455
+ evidencia: {
456
+ path: e.path, extractor_id: e.extractor_id, extraction_method: e.extraction_method, line_start: span.line_start, line_end: span.line_end,
457
+ trecho_sha256: sha256(t), trecho_inicio: t.toString('utf8').split('\n')[0].slice(0, 200),
458
+ },
459
+ veredito: null, nota: null,
460
+ });
461
+ }
462
+ }
463
+ return {
464
+ schema: exports.AMOSTRA_SCHEMA, graph_schema: grafo.schema, repository_id: grafo.repository_id,
465
+ revision: grafo.snapshot.revision, revision_unavailable_reason: grafo.snapshot.revision_unavailable_reason,
466
+ snapshot_id: grafo.snapshot.snapshot_id, graph_digest: digest, por_estrato: porEstrato, universo, arestas,
467
+ };
468
+ }
469
+ /** Confere a amostra auditada contra o indice do HEAD: aresta, trecho pelo hash, veredito com nota. */
470
+ function conferirAmostra(grafo, ler, arquivo) {
471
+ const amostra = JSON.parse(fs.readFileSync(arquivo, 'utf8'));
472
+ if (amostra.schema !== exports.AMOSTRA_SCHEMA || !Array.isArray(amostra.arestas))
473
+ return { linhas: [], falhas: [`schema da amostra: ${amostra.schema}`] };
474
+ if (!amostra.arestas.length)
475
+ return { linhas: [], falhas: ['amostra vazia: nada para conferir'] };
476
+ const nos = localizadorDe(grafo);
477
+ const indice = new Map(grafo.edges.map((a) => [chaveDaAresta(a.kind, nos.get(a.from), nos.get(a.to)), a]));
478
+ const falhas = [], estratos = new Set(), vereditos = {};
479
+ for (const item of amostra.arestas) {
480
+ const chave = chaveDaAresta(item.kind, item.from, item.to);
481
+ vereditos[String(item.veredito)] = (vereditos[String(item.veredito)] ?? 0) + 1;
482
+ estratos.add(item.estrato);
483
+ if (item.veredito !== 'supported' || typeof item.nota !== 'string' || !item.nota.trim()) {
484
+ falhas.push(`${chave}: veredito ${item.veredito ?? 'ausente'}${item.nota ? '' : ', sem nota'}`);
485
+ continue;
486
+ }
487
+ const achada = indice.get(chave);
488
+ if (!achada) {
489
+ falhas.push(`${chave}: aresta ausente no indice do HEAD`);
490
+ continue;
491
+ }
492
+ const ev = item.evidencia;
493
+ const bate = achada.evidence.some((e) => e.path === ev.path && e.extractor_id === ev.extractor_id
494
+ && e.extraction_method === ev.extraction_method && sha256(trecho(ler, e)) === ev.trecho_sha256);
495
+ if (!bate)
496
+ falhas.push(`${chave}: nenhuma evidencia com o trecho auditado`);
497
+ }
498
+ const linhas = [
499
+ `amostra: ${amostra.arestas.length} arestas em ${estratos.size} estratos, auditada na revisao ${amostra.revision ?? amostra.revision_unavailable_reason}`,
500
+ ` vereditos ${Object.entries(vereditos).sort(([x], [y]) => (0, intelligence_graph_contract_1.compararUtf8)(x, y)).map(([k, v]) => `${k}=${v}`).join(' ')}`,
501
+ ` conferidas contra o indice do HEAD: ${amostra.arestas.length - falhas.length} de ${amostra.arestas.length}`,
502
+ ];
503
+ return { linhas, falhas };
504
+ }
505
+ function amostra(ctx, p) {
506
+ if (p.valores.has('conferir') && p.valores.has('por-estrato'))
507
+ uso('--conferir e --por-estrato nao combinam');
508
+ const { grafo, raiz, cabecalho } = consultavel(ctx);
509
+ const ler = leitorDaArvore(raiz, grafo);
510
+ const arquivo = p.valores.get('conferir');
511
+ if (arquivo === undefined) {
512
+ const n = inteiro(p, 'por-estrato') ?? 3;
513
+ if (n < 1)
514
+ uso('--por-estrato precisa ser ao menos 1');
515
+ ctx.escrever(JSON.stringify(amostrar(grafo, ler, n, cabecalho.graph_digest), null, 2));
516
+ return 0;
517
+ }
518
+ const { linhas, falhas } = conferirAmostra(grafo, ler, arquivo);
519
+ if (p.bandeiras.has('json')) {
520
+ ctx.escrever(JSON.stringify({ schema: exports.AMOSTRA_SCHEMA, conferencia: linhas, falhas, aprovado: falhas.length === 0 }, null, 2));
521
+ }
522
+ else {
523
+ ctx.escrever([...linhas, ...falhas.map((f) => `FALHA ${f}`), falhas.length ? `reprovado: ${falhas.length} falha(s)` : 'aprovado'].join('\n'));
524
+ }
525
+ return falhas.length ? 1 : 0;
526
+ }
527
+ /** Sem `--tudo`, fica o indice de todo HEAD das arvores do repositorio: o estado e compartilhado entre elas. */
528
+ function limpar(ctx, p) {
529
+ let manter = [], manterRevisoes = [];
530
+ if (!p.bandeiras.has('tudo')) {
531
+ const arvore = (0, intelligence_graph_repo_1.revisaoDaArvore)(ctx.raiz);
532
+ manterRevisoes = (0, intelligence_graph_repo_1.headsDasArvores)(arvore.raiz);
533
+ if (arvore.head)
534
+ manter = [(0, intelligence_graph_index_1.chaveDoIndice)(arvore.head, (0, intelligence_graph_index_1.perfilDoIndice)(arvore.raiz, undefined, ctx.repositorio ? { repository_id: ctx.repositorio } : {}))];
535
+ }
536
+ const r = (0, intelligence_graph_index_1.limparIndices)(ctx, { manter, manterRevisoes, tudo: p.bandeiras.has('tudo') });
537
+ if (p.bandeiras.has('json'))
538
+ ctx.escrever(JSON.stringify({ schema: exports.STATUS_SCHEMA, manter, manter_revisoes: manterRevisoes, ...r }, null, 2));
539
+ else
540
+ ctx.escrever([`grafo limpar: ${r.removidos.length} removido(s), ${mb(r.bytes)}`, ...r.removidos.map((x) => ` ${x.nome} ${mb(x.bytes)}`)].join('\n'));
541
+ return 0;
542
+ }
543
+ /** KG5 (D4, D5): com `--teto-bytes` o erro tambem sai compacto; recusa de indice traz o estado e a correcao. */
544
+ const erroEmJson = (ctx, e, compacto) => {
545
+ const [codigo, ...detalhe] = String(e.message).split('\n')[0].split(': ');
546
+ const doIndice = Object.prototype.hasOwnProperty.call(ESTADO_DO_INDICE, codigo)
547
+ ? { estado_do_indice: ESTADO_DO_INDICE[codigo], correcao: exports.CORRECAO_DO_INDICE } : {};
548
+ const r = {
549
+ schema: intelligence_graph_query_1.CONSULTA_SCHEMA,
550
+ erro: { codigo, detalhe: detalhe.join(': ') || null, candidatos: e instanceof intelligence_graph_query_1.ErroDeConsulta ? e.candidatos : [], ...doIndice },
551
+ };
552
+ ctx.escrever(compacto ? JSON.stringify(r) : JSON.stringify(r, null, 2));
553
+ return 1;
554
+ };
555
+ /** `ork grafo <subcomando> ...`: devolve o codigo de saida; sem `--json`, o erro de uso ou tipado e lancado. */
556
+ function executarGrafo(argv, ctx) {
557
+ const compacto = argv.some((a) => a === '--teto-bytes' || a.startsWith('--teto-bytes='));
558
+ let p;
559
+ try {
560
+ p = lerPedido(argv);
561
+ }
562
+ catch (e) {
563
+ // Com `--json` no argv, tambem o erro de uso sai como objeto, como os demais.
564
+ if (argv.includes('--json'))
565
+ return erroEmJson(ctx, e, compacto);
566
+ throw e;
567
+ }
568
+ try {
569
+ if (p.sub === 'indexar')
570
+ return indexar(ctx, p);
571
+ if (p.sub === 'status')
572
+ return status(ctx, p);
573
+ if (p.sub === 'amostra')
574
+ return amostra(ctx, p);
575
+ if (p.sub === 'limpar')
576
+ return limpar(ctx, p);
577
+ return consultar(ctx, p);
578
+ }
579
+ catch (e) {
580
+ if (!p.bandeiras.has('json'))
581
+ throw e;
582
+ return erroEmJson(ctx, e, compacto);
583
+ }
584
+ }