@orkastery/cli 0.5.0 → 0.5.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/adapters/claude-code/README.md +11 -2
- package/adapters/claude-code/agents/ork-master.md +2 -0
- package/adapters/claude-code/commands/master.md +8 -0
- package/adapters/claude-code/commands/onboarding.md +3 -2
- package/adapters/claude-code/commands/ork.md +9 -0
- package/adapters/codex/skills/ork/SKILL.md +18 -3
- package/adapters/hermes/README.md +1 -0
- package/adapters/hermes/bin/ork-network-status.sh +10 -0
- package/adapters/hermes/hermes.plugin.json +1 -0
- package/adapters/hermes/hitl-ingress/__init__.py +2 -2
- package/adapters/hermes/skills/orkastery-devmaster/SKILL.md +6 -5
- package/adapters/openclaw/README.md +35 -4
- package/adapters/openclaw/dist/hitl-ingress.js +2 -2
- package/adapters/openclaw/dist/index.js +47 -12
- package/adapters/openclaw/openclaw.plugin.json +5 -1
- package/adapters/openclaw/package.json +2 -2
- package/adapters/openclaw/src/hitl-ingress.ts +2 -2
- package/adapters/openclaw/src/index.ts +50 -12
- package/assets/orkmind_bridge.py +117 -9
- package/dist/adapters/claude-bg.js +59 -11
- package/dist/adapters/codex.js +4 -3
- package/dist/agents-md.js +1 -0
- package/dist/auditrun.js +6 -2
- package/dist/board.js +16 -14
- package/dist/branch-de-estado.js +66 -10
- package/dist/busca-semantica.js +52 -12
- package/dist/canarios-rm057.js +149 -0
- package/dist/canarios.js +2 -0
- package/dist/ci.js +8 -1
- package/dist/claim-lint.js +3 -1
- package/dist/claims.js +39 -1
- package/dist/company-brain-context.js +158 -22
- package/dist/docs.js +110 -3
- package/dist/doctor-pulse-cron.js +194 -0
- package/dist/doctor-rede.js +149 -0
- package/dist/doctor.js +294 -37
- package/dist/entrega-pr.js +27 -8
- package/dist/esgotamentos.js +181 -0
- package/dist/experiencia.js +12 -2
- package/dist/fabrica-estado.js +35 -9
- package/dist/fabrica-publicar.js +6 -0
- package/dist/fechamento.js +20 -66
- package/dist/fix.js +3 -3
- package/dist/forja.js +31 -7
- package/dist/gates.js +5 -1
- package/dist/hitl-contract.js +59 -4
- package/dist/hitl-curto.js +11 -6
- package/dist/hitl-gates.js +5 -0
- package/dist/hitl-lote.js +5 -2
- package/dist/hitl-resumo.js +33 -1
- package/dist/hitl-tempo-parado.js +201 -0
- package/dist/hitl-texto-livre.js +5 -5
- package/dist/horario.js +27 -0
- package/dist/hosts.js +59 -13
- package/dist/impedimento.js +205 -0
- package/dist/index.js +433 -88
- package/dist/indice-vetorial.js +87 -10
- package/dist/indice.js +3 -1
- package/dist/init.js +28 -5
- package/dist/integracoes-locais.js +4 -0
- package/dist/intelligence-graph-cli.js +267 -30
- package/dist/intelligence-graph-contexto.js +215 -0
- package/dist/intelligence-graph-contract.js +71 -12
- package/dist/intelligence-graph-extract-md.js +41 -23
- package/dist/intelligence-graph-extract-ts.js +181 -11
- package/dist/intelligence-graph-extract.js +186 -9
- package/dist/intelligence-graph-index.js +205 -24
- package/dist/intelligence-graph-parsers.js +6 -4
- package/dist/intelligence-graph-repo.js +10 -0
- package/dist/leases.js +680 -87
- package/dist/ledger-stats.js +15 -1
- package/dist/ledger.js +12 -0
- package/dist/licoes.js +6 -4
- package/dist/liveness.js +2 -1
- package/dist/maestro-runtime.js +7 -1
- package/dist/manifest.js +44 -1
- package/dist/master-nota.js +14 -5
- package/dist/master.js +129 -12
- package/dist/mcp-artifacts.js +36 -3
- package/dist/mcp-git.js +80 -15
- package/dist/mcp-grafo-worker.js +93 -0
- package/dist/mcp-grafo.js +279 -0
- package/dist/mcp-install.js +2 -1
- package/dist/mcp-server.js +23 -1
- package/dist/mcp-ship.js +2 -0
- package/dist/mcp-verify.js +3 -1
- package/dist/memoria.js +48 -6
- package/dist/network-roadmap.js +195 -20
- package/dist/ocupacao.js +17 -5
- package/dist/onboarding.js +9 -4
- package/dist/orkmind.js +90 -5
- package/dist/orquestracao.js +14 -11
- package/dist/parado-no-condutor.js +1089 -0
- package/dist/phase.js +68 -15
- package/dist/policies.js +79 -3
- package/dist/portfolio.js +4 -1
- package/dist/projeto-alvo.js +5 -1
- package/dist/prompts.js +36 -1
- package/dist/prova-ativacao.js +414 -0
- package/dist/pulse-delivery.js +60 -7
- package/dist/pulse.js +99 -3
- package/dist/ratelimit.js +20 -4
- package/dist/recall.js +3 -1
- package/dist/rede-adesao.js +399 -0
- package/dist/rede-forja.js +289 -0
- package/dist/rede-projetos.js +239 -0
- package/dist/rede-status.js +336 -0
- package/dist/rede.js +980 -0
- package/dist/retry.js +69 -2
- package/dist/roadmap-status.js +162 -7
- package/dist/runtime-context.js +5 -1
- package/dist/runtime-profiles.js +22 -1
- package/dist/saida-segura.js +58 -0
- package/dist/session-watcher-claude.js +113 -23
- package/dist/sessoes-contas.js +187 -0
- package/dist/sessoes-inventario.js +6 -12
- package/dist/sessoes.js +87 -3
- package/dist/ship.js +110 -20
- package/dist/thread.js +212 -14
- package/dist/util.js +71 -4
- package/dist/verify.js +2 -2
- package/dist/worktree.js +34 -8
- package/eval/casos/onboarding.json +1 -1
- package/eval/fixtures/fx-pedido-colado/caso.json +20 -0
- package/package.json +7 -3
- package/references/definition-of-done.md +1 -1
- package/skills/README.md +1 -1
- package/skills/core/onboarding/SKILL.md +3 -2
- package/skills/core/orkastery-bootstrap/SKILL.md +2 -2
- package/skills/phases/master-metrics/SKILL.md +8 -3
- package/skills/phases/ship-release/SKILL.md +6 -3
package/dist/mcp-git.js
CHANGED
|
@@ -34,6 +34,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.HOOKS_ESTRITOS = exports.HOOKS_DO_COMMIT = exports.HOOKS_INERTES_DO_COMMIT = void 0;
|
|
37
|
+
exports.comoOGitSaiu = comoOGitSaiu;
|
|
37
38
|
exports.estadoGitMcp = estadoGitMcp;
|
|
38
39
|
exports.commitMcp = commitMcp;
|
|
39
40
|
exports.perfilGitMcp = perfil;
|
|
@@ -115,11 +116,25 @@ function ambiente(env = process.env) {
|
|
|
115
116
|
return { HOME: env.HOME, USER: env.USER, LOGNAME: env.LOGNAME,
|
|
116
117
|
XDG_CONFIG_HOME: env.XDG_CONFIG_HOME, PATH: '/usr/bin:/bin', LANG: 'C.UTF-8', TMPDIR: '/tmp' };
|
|
117
118
|
}
|
|
119
|
+
/**
|
|
120
|
+
* Fatia 2 do ensaio da 0.5.0 (P8): como o git saiu, sem o stderr, que pode citar caminho e conteudo.
|
|
121
|
+
* O sinal vem antes do erro (CHECK, rodada 1, S4): prazo estourado e saida acima do limite matam o git
|
|
122
|
+
* que ja executou; so o erro sem sinal (ENOENT, EACCES) e git que nem rodou.
|
|
123
|
+
*/
|
|
124
|
+
function comoOGitSaiu(r) {
|
|
125
|
+
const codigo = r.error ? r.error.code ?? 'erro' : '';
|
|
126
|
+
if (r.signal)
|
|
127
|
+
return `interrompido por ${r.signal}${codigo ? ` (${codigo})` : ''}`;
|
|
128
|
+
if (r.error)
|
|
129
|
+
return `nao executou (${codigo})`;
|
|
130
|
+
return `saiu ${r.status}`;
|
|
131
|
+
}
|
|
118
132
|
function git(wt, args, input) {
|
|
119
133
|
const r = (0, node_child_process_1.spawnSync)(GIT, ['--no-pager', '--literal-pathspecs', ...args], { cwd: wt, env: ambiente(),
|
|
120
134
|
encoding: 'utf8', input, timeout: 10000, killSignal: 'SIGKILL', maxBuffer: LIMITE });
|
|
135
|
+
// O subcomando e sempre o primeiro argumento interno, nunca entrada do cliente.
|
|
121
136
|
if (r.status !== 0 || r.signal || r.error)
|
|
122
|
-
falha(
|
|
137
|
+
falha(`command.failed: git ${args[0]} ${comoOGitSaiu(r)}`);
|
|
123
138
|
return r.stdout;
|
|
124
139
|
}
|
|
125
140
|
/** Lista positiva de configuração passiva. Não é blacklist de shell. */
|
|
@@ -155,25 +170,64 @@ function configPassiva(chave, valor, credenciaisInertes = false) {
|
|
|
155
170
|
return valor.length <= 2048 && !/[\x00-\x1f]/.test(valor);
|
|
156
171
|
return false;
|
|
157
172
|
}
|
|
158
|
-
/**
|
|
159
|
-
|
|
173
|
+
/**
|
|
174
|
+
* Metadados graváveis pelo Git/API não podem redirecionar escrita para fora.
|
|
175
|
+
*
|
|
176
|
+
* RM-037 (fatia 3, defeito 5): no armazém de objetos (`objects/`), arquivo regular com mais de um vínculo
|
|
177
|
+
* passa. O git nunca abre arquivo de lá para escrita: cria um temporário e renomeia, e objeto que já existe
|
|
178
|
+
* só tem o horário renovado. O vínculo não redireciona escrita, e um clone local liga os objetos por hard
|
|
179
|
+
* link (7.621 em 29/09): um clone qualquer travava o `ork_git_status` e o `ork_git_commit` de todas as
|
|
180
|
+
* threads. Fora de `objects/` (refs, logs, índice), segue um vínculo só, e a recusa diz o caminho e a receita.
|
|
181
|
+
*/
|
|
182
|
+
function arvoreMetadados(root, alvo, opcoes = {}) {
|
|
160
183
|
if (!fs.lstatSync(alvo, { throwIfNoEntry: false }))
|
|
161
184
|
return;
|
|
185
|
+
// O caminho da recusa e relativo a raiz do repositorio, onde a receita roda; com caractere fora do
|
|
186
|
+
// conjunto seguro (ref com `+` ou espaco, por exemplo), vai entre aspas simples para a receita copiar certo.
|
|
187
|
+
const rotulo = (f) => {
|
|
188
|
+
const r = path.relative(opcoes.relativoA ?? path.dirname(root), f).replace(/[\x00-\x1f\x7f]/g, '?').slice(0, 200);
|
|
189
|
+
return /^[A-Za-z0-9._/-]+$/.test(r) ? r : `'${r.replace(/'/g, "'\\''")}'`;
|
|
190
|
+
};
|
|
162
191
|
let vistos = 0;
|
|
163
192
|
const visitar = (f, profundidade) => {
|
|
164
|
-
if (++vistos > 8192
|
|
165
|
-
falha(
|
|
166
|
-
|
|
167
|
-
if (
|
|
168
|
-
falha(
|
|
193
|
+
if (++vistos > 8192)
|
|
194
|
+
falha(`metadata.unsupported: mais de 8192 entradas em ${rotulo(alvo)}` + (opcoes.armazemDeObjetos
|
|
195
|
+
? '; empacote os objetos soltos sem perda com git repack -d (git count-objects -v mostra quantos)' : ''));
|
|
196
|
+
if (profundidade > 32)
|
|
197
|
+
falha(`metadata.unsupported: mais de 32 niveis em ${rotulo(alvo)}`);
|
|
198
|
+
const st = fs.lstatSync(f, { throwIfNoEntry: false });
|
|
199
|
+
if (!st)
|
|
200
|
+
return; // Uma retomada pode remover a fila ou o candidato depois da enumeracao.
|
|
201
|
+
if (st.isSymbolicLink())
|
|
202
|
+
falha(`metadata.unsafe: link simbolico em ${rotulo(f)}`);
|
|
203
|
+
if (!st.isDirectory() && !st.isFile())
|
|
204
|
+
falha(`metadata.unsafe: ${rotulo(f)} nao e arquivo regular nem pasta`);
|
|
205
|
+
if (st.isFile() && st.nlink !== 1 && !opcoes.armazemDeObjetos)
|
|
206
|
+
falha(`metadata.unsafe: hard link em ${rotulo(f)} (${st.nlink} vinculos); ` +
|
|
207
|
+
`tire o vinculo sem perder conteudo: cp -p ${rotulo(f)} ${rotulo(f + '.tmp')} && mv ${rotulo(f + '.tmp')} ${rotulo(f)}`);
|
|
169
208
|
if (f !== root && !dentro(root, f))
|
|
170
|
-
falha(
|
|
171
|
-
if (st.isDirectory())
|
|
172
|
-
|
|
209
|
+
falha(`metadata.unsafe: ${rotulo(f)} fora de ${path.basename(root)}`);
|
|
210
|
+
if (st.isDirectory()) {
|
|
211
|
+
let entradas;
|
|
212
|
+
try {
|
|
213
|
+
entradas = fs.readdirSync(f, { encoding: 'buffer' });
|
|
214
|
+
}
|
|
215
|
+
catch (e) {
|
|
216
|
+
if (e.code === 'ENOENT')
|
|
217
|
+
return;
|
|
218
|
+
throw e;
|
|
219
|
+
}
|
|
220
|
+
for (const bytes of entradas) {
|
|
221
|
+
const n = bytes.toString('utf8');
|
|
222
|
+
// Perda de bytes nao e ENOENT transitorio: o nome decodificado pode nem apontar para esta entrada.
|
|
223
|
+
if (!Buffer.from(n, 'utf8').equals(bytes))
|
|
224
|
+
falha(`metadata.unsafe: nome fora de UTF-8 em ${rotulo(f)}`);
|
|
173
225
|
visitar(path.join(f, n), profundidade + 1);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
174
228
|
};
|
|
175
229
|
if (fs.realpathSync(alvo) !== alvo)
|
|
176
|
-
falha(
|
|
230
|
+
falha(`metadata.unsafe: ${rotulo(alvo)} passa por link simbolico`);
|
|
177
231
|
visitar(alvo, 0);
|
|
178
232
|
}
|
|
179
233
|
/** Cobre também refresh do índice e checkout/diff internos da API de estado.
|
|
@@ -223,9 +277,9 @@ function perfil(wt, comum, gitdir, id, selecionados, hooksDaOperacao = exports.H
|
|
|
223
277
|
fisico(path.dirname(comum), comum);
|
|
224
278
|
fisico(comum, gitdir);
|
|
225
279
|
for (const nome of ['objects', 'refs', 'logs', 'info'])
|
|
226
|
-
arvoreMetadados(comum, path.join(comum, nome));
|
|
280
|
+
arvoreMetadados(comum, path.join(comum, nome), { armazemDeObjetos: nome === 'objects' });
|
|
227
281
|
for (const nome of ['index', 'logs', 'refs'])
|
|
228
|
-
arvoreMetadados(gitdir, path.join(gitdir, nome));
|
|
282
|
+
arvoreMetadados(gitdir, path.join(gitdir, nome), { relativoA: path.dirname(comum) });
|
|
229
283
|
const estado = path.join(path.dirname(comum), '.orkastery');
|
|
230
284
|
for (const nome of ['leases', 'state-backups']) {
|
|
231
285
|
const f = path.join(estado, nome);
|
|
@@ -349,6 +403,8 @@ function executar(raiz, p) {
|
|
|
349
403
|
if (git(wt, ['diff', '--cached', '--name-only', '-z']).length)
|
|
350
404
|
falha('index.not-empty');
|
|
351
405
|
const regioes = [`worktree-write:${t.id}`, ...p.paths.map(f => 'path:' + f)];
|
|
406
|
+
// RM-037 (fatia 3, defeito 4): lease e fila de thread fechada sao orfaos e saem antes da conferencia.
|
|
407
|
+
(0, leases_1.podarRegioesDeThreadsFechadas)(raiz, regioes, t.id);
|
|
352
408
|
for (const nome of regioes) {
|
|
353
409
|
if ((0, leases_1.lerLease)(raiz, nome) || (0, leases_1.leasesColidentes)(raiz, nome).length)
|
|
354
410
|
falha('lease.busy');
|
|
@@ -362,7 +418,16 @@ function executar(raiz, p) {
|
|
|
362
418
|
falha('index.not-empty');
|
|
363
419
|
(0, estado_thread_1.comEstadoParaGit)(raiz, t.id, wt, () => {
|
|
364
420
|
conferir();
|
|
365
|
-
|
|
421
|
+
// Fatia 2 do ensaio da 0.5.0 (P8): caminho rastreado entra por `git add -u`, que nao consulta regra de
|
|
422
|
+
// ignore; o `git add` dele, sob regra local (`fontes/` casando `marketplaces/fontes/`), indexava e saia 1.
|
|
423
|
+
// Caminho novo segue pelo `git add`, que continua recusando o ignorado, e vai antes: recusado, os
|
|
424
|
+
// rastreados nem chegam ao indice.
|
|
425
|
+
const rastreados = new Set(git(wt, ['ls-files', '-z', '--', ...p.paths]).split('\0').filter(Boolean));
|
|
426
|
+
const antigos = p.paths.filter(f => rastreados.has(f)), novos = p.paths.filter(f => !rastreados.has(f));
|
|
427
|
+
if (novos.length)
|
|
428
|
+
git(wt, ['add', '--', ...novos]);
|
|
429
|
+
if (antigos.length)
|
|
430
|
+
git(wt, ['add', '-u', '--', ...antigos]);
|
|
366
431
|
const staged = git(wt, ['diff', '--cached', '--name-only', '-z']).split('\0').filter(Boolean).sort();
|
|
367
432
|
if (!staged.length || staged.some(f => !p.paths.includes(f)))
|
|
368
433
|
falha('index.selection');
|
|
@@ -0,0 +1,93 @@
|
|
|
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.rodarConsulta = rodarConsulta;
|
|
37
|
+
/**
|
|
38
|
+
* RM-031 KG5 (D3, D8): o worker da consulta do grafo pelo MCP.
|
|
39
|
+
*
|
|
40
|
+
* Roda num processo filho, aberto pelo `mcp-grafo.ts` com o ambiente minimo do MCP. Le `{raiz, argv}`
|
|
41
|
+
* do stdin, confere que o cwd e a raiz, aceita so as cinco consultas no argv que as tools montam (com
|
|
42
|
+
* `--json` e `--teto-bytes`), carrega o manifesto da raiz (a worktree da thread) e chama o
|
|
43
|
+
* `executarGrafo` com o argv que a CLI receberia: a resposta e a do `ork grafo`, byte a byte. O `main` do
|
|
44
|
+
* `ork` nao roda aqui, entao nada do argv vira `--projeto`, `--version` ou `--help`.
|
|
45
|
+
*
|
|
46
|
+
* E a segunda porta da familia do grafo, so pelo CLI dela (a fronteira confere). Saida 0 e resposta,
|
|
47
|
+
* 1 e recusa tipada da consulta (JSON no stdout), 2 e entrada recusada pelo proprio worker (stderr).
|
|
48
|
+
*/
|
|
49
|
+
const fs = __importStar(require("node:fs"));
|
|
50
|
+
const path = __importStar(require("node:path"));
|
|
51
|
+
const zod_1 = require("zod");
|
|
52
|
+
const intelligence_graph_cli_1 = require("./intelligence-graph-cli");
|
|
53
|
+
const manifest_1 = require("./manifest");
|
|
54
|
+
const estado_thread_1 = require("./estado-thread");
|
|
55
|
+
const mcp_grafo_1 = require("./mcp-grafo");
|
|
56
|
+
const CONSULTAS = Object.freeze({ vizinhos: 1, chamadores: 1, importadores: 1, caminho: 2, contexto: 1 });
|
|
57
|
+
const OPCAO = /^--(profundidade|sentido|tipo|limite|teto-bytes)=[a-z0-9_,]{1,120}$/;
|
|
58
|
+
const PEDIDO = zod_1.z.object({
|
|
59
|
+
raiz: zod_1.z.string().min(1).max(4096).refine((p) => path.isAbsolute(p), 'raiz absoluta'),
|
|
60
|
+
argv: zod_1.z.array(zod_1.z.string().min(1).max(2048)).min(4).max(12),
|
|
61
|
+
}).strict();
|
|
62
|
+
/** Confere o pedido e roda a consulta; erro de entrada e lancado, com o prefixo `grafo.mcp.worker`. */
|
|
63
|
+
function rodarConsulta(entrada, cwd, escrever) {
|
|
64
|
+
const lido = PEDIDO.safeParse(entrada);
|
|
65
|
+
if (!lido.success)
|
|
66
|
+
throw Error('grafo.mcp.worker: pedido fora do schema');
|
|
67
|
+
const { raiz, argv } = lido.data;
|
|
68
|
+
if (fs.realpathSync(cwd) !== fs.realpathSync(raiz))
|
|
69
|
+
throw Error('grafo.mcp.worker: o cwd nao e a raiz pedida');
|
|
70
|
+
const [sub, ...resto] = argv;
|
|
71
|
+
if (!Object.prototype.hasOwnProperty.call(CONSULTAS, sub))
|
|
72
|
+
throw Error(`grafo.mcp.worker: so as consultas ${Object.keys(CONSULTAS).join(', ')}`);
|
|
73
|
+
const posicionais = resto.slice(0, CONSULTAS[sub]), opcoes = resto.slice(CONSULTAS[sub]);
|
|
74
|
+
if (posicionais.length !== CONSULTAS[sub] || posicionais.some((p) => p.startsWith('--')))
|
|
75
|
+
throw Error('grafo.mcp.worker: no ausente ou com -- no inicio');
|
|
76
|
+
if (!opcoes.includes('--json') || !opcoes.some((o) => o.startsWith('--teto-bytes='))
|
|
77
|
+
|| opcoes.some((o) => o !== '--json' && !OPCAO.test(o)))
|
|
78
|
+
throw Error('grafo.mcp.worker: opcoes fora das que a tool monta');
|
|
79
|
+
const carregado = (0, manifest_1.exigirManifesto)(raiz);
|
|
80
|
+
if (fs.realpathSync(carregado.raiz) !== fs.realpathSync(raiz))
|
|
81
|
+
throw Error('grafo.mcp.worker: o manifesto nao e o da raiz pedida');
|
|
82
|
+
return (0, intelligence_graph_cli_1.executarGrafo)(argv, { raiz: carregado.raiz, estado: (0, estado_thread_1.raizDoEstado)(carregado.raiz), repositorio: carregado.manifesto.project.name,
|
|
83
|
+
contextoDaThread: (id) => (0, mcp_grafo_1.lerEntradaDaThread)(carregado.raiz, id), escrever });
|
|
84
|
+
}
|
|
85
|
+
if (require.main === module) {
|
|
86
|
+
try {
|
|
87
|
+
process.exitCode = rodarConsulta(JSON.parse(fs.readFileSync(0, 'utf8')), process.cwd(), (texto) => { process.stdout.write(texto); });
|
|
88
|
+
}
|
|
89
|
+
catch (e) {
|
|
90
|
+
process.stderr.write(`${e.message.split('\n')[0]}\n`);
|
|
91
|
+
process.exitCode = 2;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -0,0 +1,279 @@
|
|
|
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.LIMITE_MAXIMO_DO_MCP = exports.PROFUNDIDADE_MAXIMA_DO_MCP = exports.TIPOS_DE_ARESTA_DO_MCP = exports.PRAZO_DA_CONSULTA_MS = exports.TETO_MAXIMO = exports.TETO_MINIMO = exports.TETO_PADRAO = exports.TOOLS_DO_GRAFO = void 0;
|
|
37
|
+
exports.grafoLigado = grafoLigado;
|
|
38
|
+
exports.lerEntradaDaThread = lerEntradaDaThread;
|
|
39
|
+
exports.argvDaTool = argvDaTool;
|
|
40
|
+
exports.consultarPeloWorker = consultarPeloWorker;
|
|
41
|
+
exports.registrarConsultasDoGrafo = registrarConsultasDoGrafo;
|
|
42
|
+
/**
|
|
43
|
+
* RM-031 KG5: a consulta do grafo de codigo pelas fases, pelo MCP do projeto.
|
|
44
|
+
*
|
|
45
|
+
* Cinco tools de leitura com o contrato do `ork grafo` (D6): cada uma roda, na worktree da thread, o
|
|
46
|
+
* argv que a CLI receberia (`<consulta> ... --json --teto-bytes N`) e devolve o que ela escreve, sem
|
|
47
|
+
* transformar: a resposta e JSON `ork.code-graph-query/v0` com a evidencia de cada aresta e no maximo
|
|
48
|
+
* `tetoBytes` bytes (D4); contexto usa ork.thread-graph-context/v2. A recusa nao passa pelo teto.
|
|
49
|
+
*
|
|
50
|
+
* Desligadas por padrao (D2): so existem com `grafo.mcp: true` no manifesto da raiz, lido no startup, e
|
|
51
|
+
* cada chamada confere a flag de novo. A consulta roda num worker (D3), um processo filho por chamada,
|
|
52
|
+
* com o ambiente minimo do MCP, grupo proprio, prazo, cancelamento e teto do stdout: o servidor nao
|
|
53
|
+
* carrega o grafo nem o compilador. Este modulo nao importa a familia do grafo; o worker importa, so
|
|
54
|
+
* pelo CLI dela (D8). Erro de transporte (flag, thread, worker) vem como `{erro}`, como nas outras tools;
|
|
55
|
+
* erro da consulta vem como o JSON da CLI, com `isError`.
|
|
56
|
+
*/
|
|
57
|
+
const fs = __importStar(require("node:fs"));
|
|
58
|
+
const path = __importStar(require("node:path"));
|
|
59
|
+
const node_child_process_1 = require("node:child_process");
|
|
60
|
+
const zod_1 = require("zod");
|
|
61
|
+
const mcp_git_1 = require("./mcp-git");
|
|
62
|
+
const mcp_artifacts_1 = require("./mcp-artifacts");
|
|
63
|
+
const thread_1 = require("./thread");
|
|
64
|
+
const estado_thread_1 = require("./estado-thread");
|
|
65
|
+
exports.TOOLS_DO_GRAFO = ['ork_grafo_vizinhos', 'ork_grafo_chamadores', 'ork_grafo_importadores', 'ork_grafo_caminho', 'ork_grafo_contexto'];
|
|
66
|
+
/** D4: o teto das respostas, em bytes do JSON. */
|
|
67
|
+
exports.TETO_PADRAO = 32768;
|
|
68
|
+
exports.TETO_MINIMO = 4096;
|
|
69
|
+
exports.TETO_MAXIMO = 65536;
|
|
70
|
+
/** D3: prazo de uma consulta de ponta a ponta (a CLI leva cerca de 1 s neste repositorio). */
|
|
71
|
+
exports.PRAZO_DA_CONSULTA_MS = 60000;
|
|
72
|
+
/** A resposta cabe no teto; o que passa disso, em bytes, e erro de outra ordem, e o worker morre. */
|
|
73
|
+
const TETO_DO_STDOUT = 1024 * 1024;
|
|
74
|
+
/**
|
|
75
|
+
* O vocabulario e os limites da consulta do KG3, repetidos aqui porque este modulo nao importa a
|
|
76
|
+
* familia do grafo (D8); o teste confere que sao os mesmos do contrato v1 e da consulta.
|
|
77
|
+
*/
|
|
78
|
+
exports.TIPOS_DE_ARESTA_DO_MCP = ['contains', 'declares', 'imports', 'calls', 'references', 'derived_from'];
|
|
79
|
+
exports.PROFUNDIDADE_MAXIMA_DO_MCP = 5;
|
|
80
|
+
exports.LIMITE_MAXIMO_DO_MCP = 10_000;
|
|
81
|
+
/** D2: so o booleano `true` do manifesto da raiz liga as tools. */
|
|
82
|
+
function grafoLigado(manifesto) {
|
|
83
|
+
return manifesto.grafo?.mcp === true;
|
|
84
|
+
}
|
|
85
|
+
const threadId = zod_1.z.string().min(1).max(80).regex(/^[a-zA-Z0-9][a-zA-Z0-9._-]*$/);
|
|
86
|
+
/** D6: o parser do `ork grafo` trata `--` como opcao, entao o no nao comeca assim, como na CLI. */
|
|
87
|
+
const no = (descricao) => zod_1.z.string().min(1).max(2048)
|
|
88
|
+
.refine((v) => !v.startsWith('--') && !v.includes('\u0000'), { message: 'grafo.uso: o no nao comeca com -- nem tem NUL' })
|
|
89
|
+
.describe(descricao);
|
|
90
|
+
const DESCRICAO_DO_NO = 'arquivo, caminho#fragmento, tipo:caminho#fragmento ou nome solto';
|
|
91
|
+
const profundidade = zod_1.z.number().int().min(1).max(exports.PROFUNDIDADE_MAXIMA_DO_MCP).optional()
|
|
92
|
+
.describe('saltos (padrao 1)');
|
|
93
|
+
const limite = zod_1.z.number().int().min(1).max(exports.LIMITE_MAXIMO_DO_MCP).optional()
|
|
94
|
+
.describe('maximo de arestas, as mais perto primeiro (padrao 500)');
|
|
95
|
+
const sentido = zod_1.z.enum(['entrada', 'saida', 'ambos']).optional().describe('sentido das arestas');
|
|
96
|
+
const tipos = zod_1.z.array(zod_1.z.enum(exports.TIPOS_DE_ARESTA_DO_MCP)).min(1).max(exports.TIPOS_DE_ARESTA_DO_MCP.length).optional()
|
|
97
|
+
.describe('tipos de aresta (padrao: todos)');
|
|
98
|
+
const tetoBytes = zod_1.z.number().int().min(exports.TETO_MINIMO).max(exports.TETO_MAXIMO).optional()
|
|
99
|
+
.describe(`teto da resposta em bytes (padrao ${exports.TETO_PADRAO}); acima, saem as arestas mais longe`);
|
|
100
|
+
const COMUM = 'Leitura do grafo do HEAD da worktree da thread (o do ork grafo): so o que o extrator prova. '
|
|
101
|
+
+ 'Resposta JSON ork.code-graph-query/v0 de ate tetoBytes; sem indice do HEAD, recusa com a correcao ork grafo indexar.';
|
|
102
|
+
/** As opcoes da CLI, na ordem dela, com `=`: o valor nunca vira outro argumento. */
|
|
103
|
+
function opcoes(a) {
|
|
104
|
+
const r = [];
|
|
105
|
+
if (a.profundidade !== undefined)
|
|
106
|
+
r.push(`--profundidade=${a.profundidade}`);
|
|
107
|
+
if (a.sentido !== undefined)
|
|
108
|
+
r.push(`--sentido=${a.sentido}`);
|
|
109
|
+
if (a.tipos !== undefined)
|
|
110
|
+
r.push(`--tipo=${a.tipos.join(',')}`);
|
|
111
|
+
if (a.limite !== undefined)
|
|
112
|
+
r.push(`--limite=${a.limite}`);
|
|
113
|
+
r.push('--json', `--teto-bytes=${a.tetoBytes ?? exports.TETO_PADRAO}`);
|
|
114
|
+
return r;
|
|
115
|
+
}
|
|
116
|
+
const DEFINICOES = [
|
|
117
|
+
{
|
|
118
|
+
nome: 'ork_grafo_vizinhos',
|
|
119
|
+
descricao: `Vizinhanca de um no: arestas a ate N saltos, cada uma com extrator, metodo e evidencia (arquivo, linhas, bytes). ${COMUM}`,
|
|
120
|
+
schema: zod_1.z.object({ threadId, alvo: no(DESCRICAO_DO_NO), profundidade, sentido, tipos, limite, tetoBytes }).strict(),
|
|
121
|
+
argv: (a) => ['vizinhos', a.alvo, ...opcoes(a)],
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
nome: 'ork_grafo_chamadores',
|
|
125
|
+
descricao: `Quem chama o simbolo: arestas calls que chegam, com a evidencia. ${COMUM}`,
|
|
126
|
+
schema: zod_1.z.object({ threadId, alvo: no('simbolo (caminho#simbolo ou nome solto)'), profundidade, limite, tetoBytes }).strict(),
|
|
127
|
+
argv: (a) => ['chamadores', a.alvo, ...opcoes(a)],
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
nome: 'ork_grafo_importadores',
|
|
131
|
+
descricao: `Quem importa o arquivo ou o simbolo: arestas imports que chegam, com a evidencia. ${COMUM}`,
|
|
132
|
+
schema: zod_1.z.object({ threadId, alvo: no('arquivo ou simbolo'), profundidade, limite, tetoBytes }).strict(),
|
|
133
|
+
argv: (a) => ['importadores', a.alvo, ...opcoes(a)],
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
nome: 'ork_grafo_caminho',
|
|
137
|
+
descricao: `Menor caminho entre dois nos, cada passo com a evidencia; caminho que nao cabe no teto recusa. ${COMUM}`,
|
|
138
|
+
schema: zod_1.z.object({ threadId, de: no(`origem: ${DESCRICAO_DO_NO}`), para: no('destino, na mesma forma'), sentido, tipos, tetoBytes }).strict(),
|
|
139
|
+
argv: (a) => ['caminho', a.de, a.para, ...opcoes(a)],
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
nome: 'ork_grafo_contexto',
|
|
143
|
+
descricao: 'Pacote deterministico da thread: diff da worktree contra base (ignorado sem worktree), GOAL, PLAN e claims; fan-in agregado e evidencias compactas. '
|
|
144
|
+
+ 'JSON ork.thread-graph-context/v2 limitado em bytes, refs locais, prioridade entre arquivos e por proximidade ao diff. Sem indice do HEAD, recusa com ork grafo indexar.',
|
|
145
|
+
schema: zod_1.z.object({ threadId, tetoBytes }).strict(),
|
|
146
|
+
argv: (a) => ['contexto', a.threadId, ...opcoes(a)],
|
|
147
|
+
},
|
|
148
|
+
];
|
|
149
|
+
/** Leitura compartilhada por CLI e worker, fora da familia do grafo. Nunca escreve estado. */
|
|
150
|
+
function lerEntradaDaThread(raiz, id) {
|
|
151
|
+
if (!/^[a-zA-Z0-9][a-zA-Z0-9._-]{0,79}$/.test(id))
|
|
152
|
+
throw Error('grafo.contexto.thread-invalida: id invalido');
|
|
153
|
+
const projeto = fs.realpathSync((0, estado_thread_1.raizDoEstado)(raiz));
|
|
154
|
+
(0, mcp_artifacts_1.validarArquivosEstadoMcp)(projeto, id);
|
|
155
|
+
const t = (0, thread_1.lerThread)(projeto, id);
|
|
156
|
+
const cwd = fs.realpathSync(t.worktree ?? projeto);
|
|
157
|
+
if (cwd !== projeto && !cwd.startsWith(projeto + path.sep))
|
|
158
|
+
throw Error('grafo.contexto.escopo: worktree fora do projeto');
|
|
159
|
+
if (fs.realpathSync((0, estado_thread_1.raizDoEstado)(cwd)) !== projeto)
|
|
160
|
+
throw Error('grafo.contexto.escopo: worktree de outro repositorio');
|
|
161
|
+
// Em uma WT, consultar outra thread daria um pacote de outra arvore silenciosamente.
|
|
162
|
+
const origem = fs.realpathSync(raiz);
|
|
163
|
+
if (origem !== projeto && origem !== cwd)
|
|
164
|
+
throw Error('grafo.contexto.escopo: thread de outra worktree');
|
|
165
|
+
if (!/^(?:[a-f0-9]{40}|[a-f0-9]{64})$/.test(t.base.commit))
|
|
166
|
+
throw Error('grafo.contexto.base-invalida: thread sem commit base');
|
|
167
|
+
const git = (args) => {
|
|
168
|
+
const r = (0, node_child_process_1.spawnSync)('git', ['-c', 'core.fsmonitor=false', ...args], {
|
|
169
|
+
cwd, env: { ...(0, mcp_git_1.ambienteGitMcp)(), GIT_OPTIONAL_LOCKS: '0' }, encoding: 'utf8', timeout: 10000, maxBuffer: 8 * 1024 * 1024,
|
|
170
|
+
});
|
|
171
|
+
if (r.error || r.status !== 0)
|
|
172
|
+
throw Error('grafo.contexto.git-falhou: nao foi possivel ler HEAD/base/diff da thread');
|
|
173
|
+
return r.stdout;
|
|
174
|
+
};
|
|
175
|
+
const head = git(['rev-parse', '--verify', 'HEAD']).trim();
|
|
176
|
+
const diff = !t.worktree || cwd === projeto ? [] : git(['diff', '--no-ext-diff', '--no-textconv', '--no-renames', '--name-only', '-z', t.base.commit, '--']).split('\0').filter(Boolean);
|
|
177
|
+
const goal = (0, mcp_artifacts_1.lerArtefatoMcp)(projeto, id, 'goal').conteudo;
|
|
178
|
+
const plan = (0, mcp_artifacts_1.lerArtefatoMcp)(projeto, id, 'plan').conteudo;
|
|
179
|
+
const claims = (0, mcp_artifacts_1.listarClaimsMcp)(projeto, id).filter((c) => c.estado !== 'retirada').map(({ id, arquivo }) => ({ id, arquivo }));
|
|
180
|
+
if (head !== git(['rev-parse', '--verify', 'HEAD']).trim())
|
|
181
|
+
throw Error('grafo.contexto.revisao-mudou: HEAD mudou durante a leitura; repita');
|
|
182
|
+
return { raiz: cwd, head, entrada: { thread: id, base: t.base.commit, diff, diffEstado: !t.worktree || cwd === projeto ? 'ignorado-sem-worktree' : 'coletado-na-worktree', goal, plan, claims } };
|
|
183
|
+
}
|
|
184
|
+
/** D9: o argv que a tool monta para os argumentos dados; a medida offline usa o mesmo. */
|
|
185
|
+
function argvDaTool(nome, args) {
|
|
186
|
+
const d = DEFINICOES.find((x) => x.nome === nome);
|
|
187
|
+
if (!d)
|
|
188
|
+
throw Error(`grafo.mcp.tool-desconhecida: ${nome}`);
|
|
189
|
+
return d.argv(args);
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* D3: roda a consulta num processo filho, em grupo proprio, com o ambiente minimo do MCP. Prazo,
|
|
193
|
+
* cancelamento e stdout acima do teto matam o grupo; a promessa sempre resolve.
|
|
194
|
+
*/
|
|
195
|
+
function consultarPeloWorker(raiz, argv, opcoesDoWorker = {}) {
|
|
196
|
+
return new Promise((resolve) => {
|
|
197
|
+
if (opcoesDoWorker.signal?.aborted) {
|
|
198
|
+
resolve({ codigo: null, saida: '', erro: '', interrompido: 'cancelada', pid: null });
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
const grupo = process.platform !== 'win32';
|
|
202
|
+
const filho = (0, node_child_process_1.spawn)(process.execPath, [path.join(__dirname, 'mcp-grafo-worker.js')], {
|
|
203
|
+
cwd: raiz, env: (0, mcp_git_1.ambienteGitMcp)(), stdio: ['pipe', 'pipe', 'pipe'], detached: grupo,
|
|
204
|
+
});
|
|
205
|
+
let saida = '', erro = '', bytes = 0, terminado = false;
|
|
206
|
+
const concluir = (r) => {
|
|
207
|
+
if (terminado)
|
|
208
|
+
return;
|
|
209
|
+
terminado = true;
|
|
210
|
+
clearTimeout(prazo);
|
|
211
|
+
opcoesDoWorker.signal?.removeEventListener('abort', cancelar);
|
|
212
|
+
resolve(r);
|
|
213
|
+
};
|
|
214
|
+
const interromper = (motivo) => {
|
|
215
|
+
if (terminado)
|
|
216
|
+
return;
|
|
217
|
+
filho.stdout?.destroy();
|
|
218
|
+
filho.stderr?.destroy();
|
|
219
|
+
try {
|
|
220
|
+
if (filho.pid)
|
|
221
|
+
process.kill(grupo ? -filho.pid : filho.pid, 'SIGKILL');
|
|
222
|
+
}
|
|
223
|
+
catch { /* ja terminou */ }
|
|
224
|
+
concluir({ codigo: null, saida: '', erro, interrompido: motivo, pid: filho.pid ?? null });
|
|
225
|
+
};
|
|
226
|
+
const cancelar = () => interromper('cancelada');
|
|
227
|
+
const prazoMs = opcoesDoWorker.prazoMs ?? exports.PRAZO_DA_CONSULTA_MS;
|
|
228
|
+
const prazo = setTimeout(() => interromper(`prazo de ${prazoMs} ms`), prazoMs);
|
|
229
|
+
opcoesDoWorker.signal?.addEventListener('abort', cancelar, { once: true });
|
|
230
|
+
filho.stdout.setEncoding('utf8');
|
|
231
|
+
filho.stdout.on('data', (parte) => {
|
|
232
|
+
saida += parte;
|
|
233
|
+
bytes += Buffer.byteLength(parte);
|
|
234
|
+
if (bytes > TETO_DO_STDOUT)
|
|
235
|
+
interromper('saida acima do teto do worker');
|
|
236
|
+
});
|
|
237
|
+
filho.stderr.setEncoding('utf8');
|
|
238
|
+
filho.stderr.on('data', (parte) => { if (erro.length < 4096)
|
|
239
|
+
erro += parte; });
|
|
240
|
+
filho.stdin.on('error', () => undefined);
|
|
241
|
+
filho.on('error', (e) => concluir({ codigo: null, saida: '', erro: e.message, interrompido: null, pid: filho.pid ?? null }));
|
|
242
|
+
filho.on('close', (codigo, sinal) => concluir({ codigo: sinal ? null : codigo, saida, erro, interrompido: null, pid: filho.pid ?? null }));
|
|
243
|
+
filho.stdin.end(JSON.stringify({ raiz, argv }));
|
|
244
|
+
try {
|
|
245
|
+
if (filho.pid)
|
|
246
|
+
opcoesDoWorker.aoIniciar?.(filho.pid);
|
|
247
|
+
}
|
|
248
|
+
catch { /* gancho de quem testa: nao muda o resultado da consulta */ }
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
const texto = (t, isError = false) => ({ content: [{ type: 'text', text: t }], ...(isError ? { isError: true } : {}) });
|
|
252
|
+
/**
|
|
253
|
+
* As cinco tools, chamadas pelo servidor so com a flag ligada no startup. `carregar` e `thread` sao os
|
|
254
|
+
* do servidor: o projeto fixado e a thread conferida (escopo da sessao filha e worktree confinada).
|
|
255
|
+
*/
|
|
256
|
+
function registrarConsultasDoGrafo(registrar, contexto) {
|
|
257
|
+
// CHECK rodada 2 (N4): o confinamento compara caminhos reais, tambem quando quem registra passa outra forma da raiz.
|
|
258
|
+
const raiz = fs.realpathSync(contexto.raiz);
|
|
259
|
+
for (const d of DEFINICOES) {
|
|
260
|
+
registrar(d.nome, { description: d.descricao, inputSchema: d.schema, annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false } }, async (args, extra) => {
|
|
261
|
+
if (!grafoLigado(contexto.carregar().manifesto)) {
|
|
262
|
+
throw Error('grafo.mcp.desligado: a consulta do grafo pelo MCP esta desligada no manifesto (grafo.mcp); ligar e decisao do dono e vale para as sessoes abertas depois');
|
|
263
|
+
}
|
|
264
|
+
const t = contexto.thread(args.threadId);
|
|
265
|
+
// CHECK rodada 1 (S6): o caminho real que o worker recebe fica dentro do projeto, conferido aqui de novo.
|
|
266
|
+
const cwd = fs.realpathSync(t.worktree ?? raiz);
|
|
267
|
+
if (cwd !== raiz && !cwd.startsWith(raiz + path.sep))
|
|
268
|
+
throw Error('mcp.scope.violation: worktree fora do projeto');
|
|
269
|
+
const r = await consultarPeloWorker(cwd, d.argv(args), { signal: extra.signal, prazoMs: contexto.prazoMs });
|
|
270
|
+
if (r.interrompido)
|
|
271
|
+
throw Error(`grafo.mcp.indisponivel: consulta interrompida (${r.interrompido})`);
|
|
272
|
+
if (r.codigo === 0 && Buffer.byteLength(r.saida) <= (args.tetoBytes ?? exports.TETO_PADRAO))
|
|
273
|
+
return texto(r.saida);
|
|
274
|
+
if (r.codigo === 1 && r.saida.startsWith('{'))
|
|
275
|
+
return texto(r.saida, true);
|
|
276
|
+
throw Error(`grafo.mcp.indisponivel: o worker saiu com ${r.codigo ?? 'sinal'}${r.erro ? `: ${r.erro.split('\n')[0].slice(0, 300)}` : ''}`);
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
}
|
package/dist/mcp-install.js
CHANGED
|
@@ -200,8 +200,9 @@ function migrarEnvVarsCodex(bruto, entrada) {
|
|
|
200
200
|
return bruto.slice(0, corpoFim) + (corpo.endsWith('\n') ? '' : '\n') + `env_vars = ${valor}\n` + bruto.slice(corpoFim);
|
|
201
201
|
}
|
|
202
202
|
function instalarMcp(opcoes) {
|
|
203
|
+
// Ensaio da 0.5.0: os READMEs dos plugins mandavam `--project .`, recusado sem dizer como acertar.
|
|
203
204
|
if (!path.isAbsolute(opcoes.projeto))
|
|
204
|
-
throw Error('mcp.install.project.invalid: raiz absoluta obrigatoria');
|
|
205
|
+
throw Error('mcp.install.project.invalid: raiz absoluta obrigatoria; na raiz do projeto, use --project "$PWD"');
|
|
205
206
|
if (!['claude-code', 'codex'].includes(opcoes.host))
|
|
206
207
|
throw Error('mcp.install.host.invalid');
|
|
207
208
|
if (opcoes.transporteShip !== undefined && !['github-ssh', 'bare-local'].includes(opcoes.transporteShip))
|
package/dist/mcp-server.js
CHANGED
|
@@ -58,6 +58,7 @@ const hitl_contract_1 = require("./hitl-contract");
|
|
|
58
58
|
const hitl_presentation_1 = require("./hitl-presentation");
|
|
59
59
|
const roadmap_status_1 = require("./roadmap-status");
|
|
60
60
|
const network_roadmap_1 = require("./network-roadmap");
|
|
61
|
+
const rede_status_1 = require("./rede-status");
|
|
61
62
|
const hitl_sessions_1 = require("./hitl-sessions");
|
|
62
63
|
const hitl_local_1 = require("./hitl-local");
|
|
63
64
|
const mcp_artifacts_1 = require("./mcp-artifacts");
|
|
@@ -77,6 +78,7 @@ const runtime_profiles_1 = require("./runtime-profiles");
|
|
|
77
78
|
const mcp_maestro_1 = require("./mcp-maestro");
|
|
78
79
|
const projeto_alvo_1 = require("./projeto-alvo");
|
|
79
80
|
const mcp_experiencia_1 = require("./mcp-experiencia");
|
|
81
|
+
const mcp_grafo_1 = require("./mcp-grafo");
|
|
80
82
|
const identidade = zod_1.z.string().min(1).max(80).regex(/^[a-zA-Z0-9][a-zA-Z0-9._-]*$/);
|
|
81
83
|
const daThread = zod_1.z.object({ threadId: identidade }).strict();
|
|
82
84
|
const daDecisao = daThread.extend({ pedidoId: identidade }).strict();
|
|
@@ -106,7 +108,9 @@ const nova = zod_1.z.object({ nome: zod_1.z.string().trim().min(1).max(200),
|
|
|
106
108
|
const fase = daThread.extend({ fase: zod_1.z.enum(['GOAL', 'PLAN', 'GO', 'CHECK', 'SHIP', 'MASTER']),
|
|
107
109
|
prompt: zod_1.z.string().min(1).max(65536), runtime: zod_1.z.enum(['claude-bg', 'codex']).optional(),
|
|
108
110
|
model: zod_1.z.string().trim().min(1).max(200).optional(),
|
|
109
|
-
effort: zod_1.z.enum(['low', 'medium', 'high', 'xhigh', 'max']).optional(),
|
|
111
|
+
effort: zod_1.z.enum(['low', 'medium', 'high', 'xhigh', 'max']).optional(),
|
|
112
|
+
// RM-056 (D1): o perfil pedido, validado contra o store pelo mesmo caminho do CLI.
|
|
113
|
+
perfil: zod_1.z.string().trim().regex(/^[a-z0-9][a-z0-9._-]{0,62}$/i).optional(), dryRun: zod_1.z.boolean().optional() }).strict();
|
|
110
114
|
// Limite de tipos do conversor externo: validacao continua no schema Zod strict.
|
|
111
115
|
const converterSchema = zod_to_json_schema_1.zodToJsonSchema;
|
|
112
116
|
const resposta = (valor) => ({ content: [{ type: 'text', text: JSON.stringify(valor) }] });
|
|
@@ -296,6 +300,10 @@ function criarServidorMcp(opcoes) {
|
|
|
296
300
|
};
|
|
297
301
|
const livre = (id) => {
|
|
298
302
|
const nomes = ['main-tree', ...(id ? ['worktree-write:' + id] : [])];
|
|
303
|
+
// RM-037 (fatia 3, GO-FIX 2, achado 1 do CHECK): esta e a primeira conferencia de toda tool de escrita.
|
|
304
|
+
// Lease e fila de thread fechada sao orfaos e saem aqui, com registro no ledger dela; sem isso o
|
|
305
|
+
// main-tree de um SHIP que caiu antes do fechamento travava verify, commit, ship e artefato pelo MCP.
|
|
306
|
+
(0, leases_1.podarRegioesDeThreadsFechadas)(raiz, nomes, id ?? '');
|
|
299
307
|
for (const nome of nomes) {
|
|
300
308
|
const conflitos = (0, leases_1.leasesColidentes)(raiz, nome, id);
|
|
301
309
|
if (conflitos.length)
|
|
@@ -497,6 +505,16 @@ function criarServidorMcp(opcoes) {
|
|
|
497
505
|
const panorama = (0, network_roadmap_1.montarPanoramaDaRede)({ fixado: raiz });
|
|
498
506
|
return resposta({ texto: (0, network_roadmap_1.textoDoPanoramaDaRede)(panorama), panorama });
|
|
499
507
|
});
|
|
508
|
+
/**
|
|
509
|
+
* RM-054 (fatia 3): as maquinas da pessoa (RM-053, `ork.rede-status/v1`), com a fonte, a batida e as
|
|
510
|
+
* lacunas. O retrato de cada maquina fica so com o projeto servido (D5 da RM-052).
|
|
511
|
+
*/
|
|
512
|
+
registrarTool('ork_network_status', { description: 'Status da Orkastery Network, somente leitura: as maquinas da pessoa (casa privada na forja), com a batida de cada uma, a forja e o login, os runtimes e hosts com versao e o projeto servido em cada uma, mais a fonte e as lacunas. Transporte o texto como vem. Lacuna e "Nao consultado" sao o que nao foi lido: nunca conclua "nenhuma maquina" nem "rede vazia" a partir deles. Nao le roadmap nem threads: para isso, ork_network_roadmap.',
|
|
513
|
+
inputSchema: zod_1.z.object({}).strict(), annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: true } }, async () => {
|
|
514
|
+
carregar();
|
|
515
|
+
const status = (0, network_roadmap_1.redeDoProjetoFixado)(raiz);
|
|
516
|
+
return resposta({ texto: (0, rede_status_1.textoDaRede)(status), status: JSON.parse((0, rede_status_1.jsonDaRede)(status)) });
|
|
517
|
+
});
|
|
500
518
|
registrarTool('ork_observe', { description: 'Observa uma vez o progresso canonico e pedidos da thread. Nao cria monitor ou despacho.',
|
|
501
519
|
inputSchema: daThread, annotations: { readOnlyHint: true } }, async ({ threadId }) => {
|
|
502
520
|
return resposta(observar(threadId));
|
|
@@ -540,6 +558,10 @@ function criarServidorMcp(opcoes) {
|
|
|
540
558
|
thread(threadId);
|
|
541
559
|
return resposta(await ingresso.solicitar(threadId, pedidoId, extra.signal));
|
|
542
560
|
});
|
|
561
|
+
// RM-031 KG5 (D2): as tools do grafo so existem com a flag do manifesto da raiz no startup; sem ela, nada muda.
|
|
562
|
+
// CHECK rodada 1 (S8): o startup le so o manifesto, como antes; as conferencias de estado ficam na chamada.
|
|
563
|
+
if ((0, mcp_grafo_1.grafoLigado)((0, manifest_1.exigirManifesto)(raiz).manifesto))
|
|
564
|
+
(0, mcp_grafo_1.registrarConsultasDoGrafo)(registrarTool, { raiz, carregar, thread });
|
|
543
565
|
return server;
|
|
544
566
|
}
|
|
545
567
|
async function servirMcp(opcoes) {
|
package/dist/mcp-ship.js
CHANGED
|
@@ -324,6 +324,8 @@ function executar(f, p) {
|
|
|
324
324
|
if (comandos.length > 512 || comandos.reduce((n, s) => n + Buffer.byteLength(s), 0) > LIMITE)
|
|
325
325
|
erro('verify.limit');
|
|
326
326
|
const nome = `worktree-write:${t.id}`, nomes = [nome, `path:.orkastery/threads/${t.id}`];
|
|
327
|
+
// RM-037 (fatia 3, defeito 4): lease e fila de thread fechada sao orfaos e saem antes da conferencia.
|
|
328
|
+
(0, leases_1.podarRegioesDeThreadsFechadas)(raiz, [...nomes, 'main-tree'], t.id);
|
|
327
329
|
for (const n of [...nomes, 'main-tree'])
|
|
328
330
|
if (fs.lstatSync((0, leases_1.caminhoLease)(raiz, n), { throwIfNoEntry: false }) || (0, leases_1.leasesColidentes)(raiz, n).length)
|
|
329
331
|
erro('lease.busy');
|
package/dist/mcp-verify.js
CHANGED
|
@@ -97,7 +97,9 @@ function verificarMcp(raizEntrada, threadId, conducao = {}) {
|
|
|
97
97
|
const stFila = fs.lstatSync((0, leases_1.caminhoFila)(raiz), { throwIfNoEntry: false });
|
|
98
98
|
if (stFila && (!stFila.isFile() || stFila.nlink !== 1))
|
|
99
99
|
throw Error('mcp.verify.queue.unsafe');
|
|
100
|
-
//
|
|
100
|
+
// RM-037 (fatia 3, defeito 4): lease e fila de thread fechada são órfãos e saem antes da conferência.
|
|
101
|
+
(0, leases_1.podarRegioesDeThreadsFechadas)(raiz, [...nomes, 'main-tree'], threadId);
|
|
102
|
+
// Não herda nem remove lease existente de thread aberta, inclusive própria, expirada ou corrompida.
|
|
101
103
|
if (nomes.some(nome => fs.lstatSync((0, leases_1.caminhoLease)(raiz, nome), { throwIfNoEntry: false }) ||
|
|
102
104
|
(0, leases_1.leasesColidentes)(raiz, nome).length) || (0, leases_1.leasesColidentes)(raiz, 'main-tree').length) {
|
|
103
105
|
throw Error('lease.busy: verificação exige leases próprias livres');
|