@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,164 @@
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.consultaExperienciaSchema = void 0;
37
+ exports.apresentarConsulta = apresentarConsulta;
38
+ exports.consultarPainel = consultarPainel;
39
+ exports.criarLeitorExperiencia = criarLeitorExperiencia;
40
+ exports.registrarConsultasExperiencia = registrarConsultasExperiencia;
41
+ /** Consultas do projeto fixado. Nunca reserva item, publica máquina ou executa push. */
42
+ const fs = __importStar(require("node:fs"));
43
+ const path = __importStar(require("node:path"));
44
+ const node_child_process_1 = require("node:child_process");
45
+ const node_crypto_1 = require("node:crypto");
46
+ const zod_1 = require("zod");
47
+ const roadmap_reservas_1 = require("./roadmap-reservas");
48
+ const fabrica_estado_1 = require("./fabrica-estado");
49
+ const mcp_ship_1 = require("./mcp-ship");
50
+ const mcp_git_1 = require("./mcp-git");
51
+ exports.consultaExperienciaSchema = zod_1.z.object({}).strict();
52
+ function apresentarConsulta(painel) {
53
+ const estado = painel.atualizado ? 'atualizado' : painel.ponta ? 'desatualizado' : 'indisponivel';
54
+ return { estado, dados: estado === 'indisponivel' ? null : painel,
55
+ motivo: estado === 'atualizado' ? null : 'mcp.experiencia.remoto.indisponivel' };
56
+ }
57
+ const indisponivel = () => ({ estado: 'indisponivel', dados: null, motivo: 'mcp.experiencia.consulta.indisponivel' });
58
+ /** Chamada somente depois de validar a fixação do worker. */
59
+ function consultarPainel(raiz, tipo) {
60
+ try {
61
+ return apresentarConsulta(tipo === 'reservas' ? (0, roadmap_reservas_1.listarReservas)(raiz, { remoto: 'origin' })
62
+ : (0, fabrica_estado_1.lerFabrica)(raiz, { remoto: 'origin', timeoutMs: 15000 }));
63
+ }
64
+ catch {
65
+ return indisponivel();
66
+ }
67
+ }
68
+ /** Usa as mesmas guardas de transporte do núcleo; não cria autorização de SHIP. */
69
+ function assinatura(raiz, transporte) {
70
+ (0, mcp_ship_1.criarPerfilShipMcp)(raiz, transporte);
71
+ const g = path.join(raiz, '.git');
72
+ (0, mcp_git_1.perfilGitMcp)(raiz, g, g, 'consulta-experiencia', []);
73
+ const st = fs.statSync(raiz);
74
+ return (0, node_crypto_1.createHash)('sha256').update(JSON.stringify([st.dev, st.ino, fs.realpathSync(raiz),
75
+ (0, mcp_git_1.gitPassivoMcp)(raiz, ['remote', 'get-url', '--all', 'origin']),
76
+ (0, mcp_git_1.gitPassivoMcp)(raiz, ['config', '--null', '--list'])])).digest('hex');
77
+ }
78
+ const PRAZO_DA_CONSULTA_MS = 90000;
79
+ /**
80
+ * O worker herda somente ambiente permitido; nunca modifica env do servidor. Roda assíncrono para
81
+ * o servidor MCP seguir atendendo as outras tools durante a consulta remota, e em grupo próprio:
82
+ * prazo ou cancelamento da chamada matam o worker e o git que ele abriu.
83
+ */
84
+ function criarLeitorExperiencia(raiz, transporte, prazoMs = PRAZO_DA_CONSULTA_MS) {
85
+ let fixacao = null;
86
+ try {
87
+ fixacao = assinatura(raiz, transporte);
88
+ }
89
+ catch { /* ferramenta continua descoberta, com lacuna explícita */ }
90
+ return (tipo, signal) => new Promise(resolve => {
91
+ if (!fixacao || signal?.aborted) {
92
+ resolve(indisponivel());
93
+ return;
94
+ }
95
+ const grupo = process.platform !== 'win32';
96
+ const filho = (0, node_child_process_1.spawn)(process.execPath, [__filename, '--consulta-experiencia'], {
97
+ cwd: raiz, env: { ...(0, mcp_git_1.ambienteGitMcp)(), SSH_AUTH_SOCK: process.env.SSH_AUTH_SOCK },
98
+ stdio: ['pipe', 'pipe', 'ignore'], detached: grupo,
99
+ });
100
+ let saida = '', terminado = false;
101
+ const concluir = (r) => {
102
+ if (terminado)
103
+ return;
104
+ terminado = true;
105
+ clearTimeout(prazo);
106
+ signal?.removeEventListener('abort', abortar);
107
+ resolve(r);
108
+ };
109
+ const abortar = () => {
110
+ if (terminado)
111
+ return;
112
+ filho.stdout?.destroy();
113
+ try {
114
+ if (filho.pid)
115
+ process.kill(grupo ? -filho.pid : filho.pid, 'SIGKILL');
116
+ }
117
+ catch { /* já terminou */ }
118
+ concluir(indisponivel());
119
+ };
120
+ const prazo = setTimeout(abortar, prazoMs);
121
+ signal?.addEventListener('abort', abortar, { once: true });
122
+ filho.stdout.setEncoding('utf8');
123
+ filho.stdout.on('data', (parte) => { saida += parte; if (saida.length > 1024 * 1024)
124
+ abortar(); });
125
+ filho.stdin.on('error', () => undefined);
126
+ filho.on('error', () => concluir(indisponivel()));
127
+ filho.on('close', (codigo, sinal) => {
128
+ if (codigo !== 0 || sinal) {
129
+ concluir(indisponivel());
130
+ return;
131
+ }
132
+ try {
133
+ concluir(JSON.parse(saida));
134
+ }
135
+ catch {
136
+ concluir(indisponivel());
137
+ }
138
+ });
139
+ filho.stdin.end(JSON.stringify({ raiz, transporte, fixacao, tipo }));
140
+ });
141
+ }
142
+ function registrarConsultasExperiencia(registrar, opcoes) {
143
+ const ler = criarLeitorExperiencia(opcoes.raiz, opcoes.transporte);
144
+ for (const [nome, tipo] of [['ork_roadmap_reservas', 'reservas'], ['ork_fabrica', 'fabrica']]) {
145
+ registrar(nome, { description: 'Consulta de ' + tipo + ' do projeto e origin fixados; distingue remoto atualizado, cópia desatualizada e indisponibilidade. Não reserva nem publica.',
146
+ inputSchema: exports.consultaExperienciaSchema, annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: true } }, async (_args, extra) => {
147
+ opcoes.carregar();
148
+ return { content: [{ type: 'text', text: JSON.stringify(await ler(tipo, extra.signal)) }] };
149
+ });
150
+ }
151
+ }
152
+ if (require.main === module && process.argv[2] === '--consulta-experiencia') {
153
+ try {
154
+ const p = zod_1.z.object({ raiz: zod_1.z.string(), transporte: zod_1.z.enum(['github-ssh', 'bare-local']),
155
+ fixacao: zod_1.z.string().regex(/^[a-f0-9]{64}$/), tipo: zod_1.z.enum(['reservas', 'fabrica']) }).strict()
156
+ .parse(JSON.parse(fs.readFileSync(0, 'utf8')));
157
+ if (assinatura(p.raiz, p.transporte) !== p.fixacao)
158
+ throw Error('scope.changed');
159
+ process.stdout.write(JSON.stringify(consultarPainel(p.raiz, p.tipo)));
160
+ }
161
+ catch {
162
+ process.stdout.write(JSON.stringify(indisponivel()));
163
+ }
164
+ }
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('command.failed');
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. */
@@ -131,6 +146,10 @@ function configPassiva(chave, valor, credenciaisInertes = false) {
131
146
  return valor.length <= 8192 && !valor.includes('\0');
132
147
  if (['user.name', 'user.email'].includes(chave))
133
148
  return !!valor.trim() && valor.length <= 512 && !/[\x00-\x1f]/.test(valor);
149
+ // RM-051: só afrouxa a checagem de dono, e o Git só a lê no sistema ou no global; o repositório
150
+ // continua conferido chave a chave aqui. A imagem do runner hospedado do GitHub grava `safe.directory=*`.
151
+ if (chave === 'safe.directory')
152
+ return valor.length <= 4096 && !/[\x00-\x1f\x7f]/.test(valor);
134
153
  // RM-037 (defeitosdeco D-3): a seção do próprio produto (por exemplo `orkastery.cortefeito`, lida
135
154
  // pela trava do corte no pre-push). O Git nunca lê essa seção, então ela não executa nada.
136
155
  if (/^orkastery\.[a-z0-9-]+$/.test(chave))
@@ -151,25 +170,47 @@ function configPassiva(chave, valor, credenciaisInertes = false) {
151
170
  return valor.length <= 2048 && !/[\x00-\x1f]/.test(valor);
152
171
  return false;
153
172
  }
154
- /** Metadados graváveis pelo Git/API não podem redirecionar escrita para fora. */
155
- function arvoreMetadados(root, alvo) {
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 = {}) {
156
183
  if (!fs.lstatSync(alvo, { throwIfNoEntry: false }))
157
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
+ };
158
191
  let vistos = 0;
159
192
  const visitar = (f, profundidade) => {
160
- if (++vistos > 8192 || profundidade > 32)
161
- falha('metadata.unsupported');
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)}`);
162
198
  const st = fs.lstatSync(f);
163
- if (st.isSymbolicLink() || (!st.isDirectory() && (!st.isFile() || st.nlink !== 1)))
164
- falha('metadata.unsafe');
199
+ if (st.isSymbolicLink())
200
+ falha(`metadata.unsafe: link simbolico em ${rotulo(f)}`);
201
+ if (!st.isDirectory() && !st.isFile())
202
+ falha(`metadata.unsafe: ${rotulo(f)} nao e arquivo regular nem pasta`);
203
+ if (st.isFile() && st.nlink !== 1 && !opcoes.armazemDeObjetos)
204
+ falha(`metadata.unsafe: hard link em ${rotulo(f)} (${st.nlink} vinculos); ` +
205
+ `tire o vinculo sem perder conteudo: cp -p ${rotulo(f)} ${rotulo(f + '.tmp')} && mv ${rotulo(f + '.tmp')} ${rotulo(f)}`);
165
206
  if (f !== root && !dentro(root, f))
166
- falha('metadata.unsafe');
207
+ falha(`metadata.unsafe: ${rotulo(f)} fora de ${path.basename(root)}`);
167
208
  if (st.isDirectory())
168
209
  for (const n of fs.readdirSync(f))
169
210
  visitar(path.join(f, n), profundidade + 1);
170
211
  };
171
212
  if (fs.realpathSync(alvo) !== alvo)
172
- falha('metadata.unsafe');
213
+ falha(`metadata.unsafe: ${rotulo(alvo)} passa por link simbolico`);
173
214
  visitar(alvo, 0);
174
215
  }
175
216
  /** Cobre também refresh do índice e checkout/diff internos da API de estado.
@@ -219,9 +260,9 @@ function perfil(wt, comum, gitdir, id, selecionados, hooksDaOperacao = exports.H
219
260
  fisico(path.dirname(comum), comum);
220
261
  fisico(comum, gitdir);
221
262
  for (const nome of ['objects', 'refs', 'logs', 'info'])
222
- arvoreMetadados(comum, path.join(comum, nome));
263
+ arvoreMetadados(comum, path.join(comum, nome), { armazemDeObjetos: nome === 'objects' });
223
264
  for (const nome of ['index', 'logs', 'refs'])
224
- arvoreMetadados(gitdir, path.join(gitdir, nome));
265
+ arvoreMetadados(gitdir, path.join(gitdir, nome), { relativoA: path.dirname(comum) });
225
266
  const estado = path.join(path.dirname(comum), '.orkastery');
226
267
  for (const nome of ['leases', 'state-backups']) {
227
268
  const f = path.join(estado, nome);
@@ -345,6 +386,8 @@ function executar(raiz, p) {
345
386
  if (git(wt, ['diff', '--cached', '--name-only', '-z']).length)
346
387
  falha('index.not-empty');
347
388
  const regioes = [`worktree-write:${t.id}`, ...p.paths.map(f => 'path:' + f)];
389
+ // RM-037 (fatia 3, defeito 4): lease e fila de thread fechada sao orfaos e saem antes da conferencia.
390
+ (0, leases_1.podarRegioesDeThreadsFechadas)(raiz, regioes, t.id);
348
391
  for (const nome of regioes) {
349
392
  if ((0, leases_1.lerLease)(raiz, nome) || (0, leases_1.leasesColidentes)(raiz, nome).length)
350
393
  falha('lease.busy');
@@ -358,7 +401,16 @@ function executar(raiz, p) {
358
401
  falha('index.not-empty');
359
402
  (0, estado_thread_1.comEstadoParaGit)(raiz, t.id, wt, () => {
360
403
  conferir();
361
- git(wt, ['add', '--', ...p.paths]);
404
+ // Fatia 2 do ensaio da 0.5.0 (P8): caminho rastreado entra por `git add -u`, que nao consulta regra de
405
+ // ignore; o `git add` dele, sob regra local (`fontes/` casando `marketplaces/fontes/`), indexava e saia 1.
406
+ // Caminho novo segue pelo `git add`, que continua recusando o ignorado, e vai antes: recusado, os
407
+ // rastreados nem chegam ao indice.
408
+ const rastreados = new Set(git(wt, ['ls-files', '-z', '--', ...p.paths]).split('\0').filter(Boolean));
409
+ const antigos = p.paths.filter(f => rastreados.has(f)), novos = p.paths.filter(f => !rastreados.has(f));
410
+ if (novos.length)
411
+ git(wt, ['add', '--', ...novos]);
412
+ if (antigos.length)
413
+ git(wt, ['add', '-u', '--', ...antigos]);
362
414
  const staged = git(wt, ['diff', '--cached', '--name-only', '-z']).split('\0').filter(Boolean).sort();
363
415
  if (!staged.length || staged.some(f => !p.paths.includes(f)))
364
416
  falha('index.selection');
@@ -0,0 +1,91 @@
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 quatro 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 CONSULTAS = Object.freeze({ vizinhos: 1, chamadores: 1, importadores: 1, caminho: 2 });
56
+ const OPCAO = /^--(profundidade|sentido|tipo|limite|teto-bytes)=[a-z0-9_,]{1,120}$/;
57
+ const PEDIDO = zod_1.z.object({
58
+ raiz: zod_1.z.string().min(1).max(4096).refine((p) => path.isAbsolute(p), 'raiz absoluta'),
59
+ argv: zod_1.z.array(zod_1.z.string().min(1).max(2048)).min(4).max(12),
60
+ }).strict();
61
+ /** Confere o pedido e roda a consulta; erro de entrada e lancado, com o prefixo `grafo.mcp.worker`. */
62
+ function rodarConsulta(entrada, cwd, escrever) {
63
+ const lido = PEDIDO.safeParse(entrada);
64
+ if (!lido.success)
65
+ throw Error('grafo.mcp.worker: pedido fora do schema');
66
+ const { raiz, argv } = lido.data;
67
+ if (fs.realpathSync(cwd) !== fs.realpathSync(raiz))
68
+ throw Error('grafo.mcp.worker: o cwd nao e a raiz pedida');
69
+ const [sub, ...resto] = argv;
70
+ if (!Object.prototype.hasOwnProperty.call(CONSULTAS, sub))
71
+ throw Error(`grafo.mcp.worker: so as consultas ${Object.keys(CONSULTAS).join(', ')}`);
72
+ const posicionais = resto.slice(0, CONSULTAS[sub]), opcoes = resto.slice(CONSULTAS[sub]);
73
+ if (posicionais.length !== CONSULTAS[sub] || posicionais.some((p) => p.startsWith('--')))
74
+ throw Error('grafo.mcp.worker: no ausente ou com -- no inicio');
75
+ if (!opcoes.includes('--json') || !opcoes.some((o) => o.startsWith('--teto-bytes='))
76
+ || opcoes.some((o) => o !== '--json' && !OPCAO.test(o)))
77
+ throw Error('grafo.mcp.worker: opcoes fora das que a tool monta');
78
+ const carregado = (0, manifest_1.exigirManifesto)(raiz);
79
+ if (fs.realpathSync(carregado.raiz) !== fs.realpathSync(raiz))
80
+ throw Error('grafo.mcp.worker: o manifesto nao e o da raiz pedida');
81
+ return (0, intelligence_graph_cli_1.executarGrafo)(argv, { raiz: carregado.raiz, estado: (0, estado_thread_1.raizDoEstado)(carregado.raiz), repositorio: carregado.manifesto.project.name, escrever });
82
+ }
83
+ if (require.main === module) {
84
+ try {
85
+ process.exitCode = rodarConsulta(JSON.parse(fs.readFileSync(0, 'utf8')), process.cwd(), (texto) => { process.stdout.write(texto); });
86
+ }
87
+ catch (e) {
88
+ process.stderr.write(`${e.message.split('\n')[0]}\n`);
89
+ process.exitCode = 2;
90
+ }
91
+ }
@@ -0,0 +1,233 @@
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.argvDaTool = argvDaTool;
39
+ exports.consultarPeloWorker = consultarPeloWorker;
40
+ exports.registrarConsultasDoGrafo = registrarConsultasDoGrafo;
41
+ /**
42
+ * RM-031 KG5: a consulta do grafo de codigo pelas fases, pelo MCP do projeto.
43
+ *
44
+ * Quatro tools de leitura com o contrato do `ork grafo` (D6): cada uma roda, na worktree da thread, o
45
+ * argv que a CLI receberia (`<consulta> ... --json --teto-bytes N`) e devolve o que ela escreve, sem
46
+ * transformar: a resposta e JSON `ork.code-graph-query/v0` com a evidencia de cada aresta e no maximo
47
+ * `tetoBytes` bytes (D4); a recusa da consulta sai compacta e nao passa pelo teto.
48
+ *
49
+ * Desligadas por padrao (D2): so existem com `grafo.mcp: true` no manifesto da raiz, lido no startup, e
50
+ * cada chamada confere a flag de novo. A consulta roda num worker (D3), um processo filho por chamada,
51
+ * com o ambiente minimo do MCP, grupo proprio, prazo, cancelamento e teto do stdout: o servidor nao
52
+ * carrega o grafo nem o compilador. Este modulo nao importa a familia do grafo; o worker importa, so
53
+ * pelo CLI dela (D8). Erro de transporte (flag, thread, worker) vem como `{erro}`, como nas outras tools;
54
+ * erro da consulta vem como o JSON da CLI, com `isError`.
55
+ */
56
+ const fs = __importStar(require("node:fs"));
57
+ const path = __importStar(require("node:path"));
58
+ const node_child_process_1 = require("node:child_process");
59
+ const zod_1 = require("zod");
60
+ const mcp_git_1 = require("./mcp-git");
61
+ exports.TOOLS_DO_GRAFO = ['ork_grafo_vizinhos', 'ork_grafo_chamadores', 'ork_grafo_importadores', 'ork_grafo_caminho'];
62
+ /** D4: o teto das respostas, em bytes do JSON. */
63
+ exports.TETO_PADRAO = 32768;
64
+ exports.TETO_MINIMO = 4096;
65
+ exports.TETO_MAXIMO = 65536;
66
+ /** D3: prazo de uma consulta de ponta a ponta (a CLI leva cerca de 1 s neste repositorio). */
67
+ exports.PRAZO_DA_CONSULTA_MS = 60000;
68
+ /** A resposta cabe no teto; o que passa disso, em bytes, e erro de outra ordem, e o worker morre. */
69
+ const TETO_DO_STDOUT = 1024 * 1024;
70
+ /**
71
+ * O vocabulario e os limites da consulta do KG3, repetidos aqui porque este modulo nao importa a
72
+ * familia do grafo (D8); o teste confere que sao os mesmos do contrato v1 e da consulta.
73
+ */
74
+ exports.TIPOS_DE_ARESTA_DO_MCP = ['contains', 'declares', 'imports', 'calls', 'references', 'derived_from'];
75
+ exports.PROFUNDIDADE_MAXIMA_DO_MCP = 5;
76
+ exports.LIMITE_MAXIMO_DO_MCP = 10_000;
77
+ /** D2: so o booleano `true` do manifesto da raiz liga as tools. */
78
+ function grafoLigado(manifesto) {
79
+ return manifesto.grafo?.mcp === true;
80
+ }
81
+ const threadId = zod_1.z.string().min(1).max(80).regex(/^[a-zA-Z0-9][a-zA-Z0-9._-]*$/);
82
+ /** D6: o parser do `ork grafo` trata `--` como opcao, entao o no nao comeca assim, como na CLI. */
83
+ const no = (descricao) => zod_1.z.string().min(1).max(2048)
84
+ .refine((v) => !v.startsWith('--') && !v.includes('\u0000'), { message: 'grafo.uso: o no nao comeca com -- nem tem NUL' })
85
+ .describe(descricao);
86
+ const DESCRICAO_DO_NO = 'arquivo, caminho#fragmento, tipo:caminho#fragmento ou nome solto';
87
+ const profundidade = zod_1.z.number().int().min(1).max(exports.PROFUNDIDADE_MAXIMA_DO_MCP).optional()
88
+ .describe('saltos (padrao 1)');
89
+ const limite = zod_1.z.number().int().min(1).max(exports.LIMITE_MAXIMO_DO_MCP).optional()
90
+ .describe('maximo de arestas, as mais perto primeiro (padrao 500)');
91
+ const sentido = zod_1.z.enum(['entrada', 'saida', 'ambos']).optional().describe('sentido das arestas');
92
+ 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()
93
+ .describe('tipos de aresta (padrao: todos)');
94
+ const tetoBytes = zod_1.z.number().int().min(exports.TETO_MINIMO).max(exports.TETO_MAXIMO).optional()
95
+ .describe(`teto da resposta em bytes (padrao ${exports.TETO_PADRAO}); acima, saem as arestas mais longe`);
96
+ const COMUM = 'Leitura do grafo do HEAD da worktree da thread (o do ork grafo): so o que o extrator prova. '
97
+ + 'Resposta JSON ork.code-graph-query/v0 de ate tetoBytes; sem indice do HEAD, recusa com a correcao ork grafo indexar.';
98
+ /** As opcoes da CLI, na ordem dela, com `=`: o valor nunca vira outro argumento. */
99
+ function opcoes(a) {
100
+ const r = [];
101
+ if (a.profundidade !== undefined)
102
+ r.push(`--profundidade=${a.profundidade}`);
103
+ if (a.sentido !== undefined)
104
+ r.push(`--sentido=${a.sentido}`);
105
+ if (a.tipos !== undefined)
106
+ r.push(`--tipo=${a.tipos.join(',')}`);
107
+ if (a.limite !== undefined)
108
+ r.push(`--limite=${a.limite}`);
109
+ r.push('--json', `--teto-bytes=${a.tetoBytes ?? exports.TETO_PADRAO}`);
110
+ return r;
111
+ }
112
+ const DEFINICOES = [
113
+ {
114
+ nome: 'ork_grafo_vizinhos',
115
+ descricao: `Vizinhanca de um no: arestas a ate N saltos, cada uma com extrator, metodo e evidencia (arquivo, linhas, bytes). ${COMUM}`,
116
+ schema: zod_1.z.object({ threadId, alvo: no(DESCRICAO_DO_NO), profundidade, sentido, tipos, limite, tetoBytes }).strict(),
117
+ argv: (a) => ['vizinhos', a.alvo, ...opcoes(a)],
118
+ },
119
+ {
120
+ nome: 'ork_grafo_chamadores',
121
+ descricao: `Quem chama o simbolo: arestas calls que chegam, com a evidencia. ${COMUM}`,
122
+ schema: zod_1.z.object({ threadId, alvo: no('simbolo (caminho#simbolo ou nome solto)'), profundidade, limite, tetoBytes }).strict(),
123
+ argv: (a) => ['chamadores', a.alvo, ...opcoes(a)],
124
+ },
125
+ {
126
+ nome: 'ork_grafo_importadores',
127
+ descricao: `Quem importa o arquivo ou o simbolo: arestas imports que chegam, com a evidencia. ${COMUM}`,
128
+ schema: zod_1.z.object({ threadId, alvo: no('arquivo ou simbolo'), profundidade, limite, tetoBytes }).strict(),
129
+ argv: (a) => ['importadores', a.alvo, ...opcoes(a)],
130
+ },
131
+ {
132
+ nome: 'ork_grafo_caminho',
133
+ descricao: `Menor caminho entre dois nos, cada passo com a evidencia; caminho que nao cabe no teto recusa. ${COMUM}`,
134
+ schema: zod_1.z.object({ threadId, de: no(`origem: ${DESCRICAO_DO_NO}`), para: no('destino, na mesma forma'), sentido, tipos, tetoBytes }).strict(),
135
+ argv: (a) => ['caminho', a.de, a.para, ...opcoes(a)],
136
+ },
137
+ ];
138
+ /** D9: o argv que a tool monta para os argumentos dados; a medida offline usa o mesmo. */
139
+ function argvDaTool(nome, args) {
140
+ const d = DEFINICOES.find((x) => x.nome === nome);
141
+ if (!d)
142
+ throw Error(`grafo.mcp.tool-desconhecida: ${nome}`);
143
+ return d.argv(args);
144
+ }
145
+ /**
146
+ * D3: roda a consulta num processo filho, em grupo proprio, com o ambiente minimo do MCP. Prazo,
147
+ * cancelamento e stdout acima do teto matam o grupo; a promessa sempre resolve.
148
+ */
149
+ function consultarPeloWorker(raiz, argv, opcoesDoWorker = {}) {
150
+ return new Promise((resolve) => {
151
+ if (opcoesDoWorker.signal?.aborted) {
152
+ resolve({ codigo: null, saida: '', erro: '', interrompido: 'cancelada', pid: null });
153
+ return;
154
+ }
155
+ const grupo = process.platform !== 'win32';
156
+ const filho = (0, node_child_process_1.spawn)(process.execPath, [path.join(__dirname, 'mcp-grafo-worker.js')], {
157
+ cwd: raiz, env: (0, mcp_git_1.ambienteGitMcp)(), stdio: ['pipe', 'pipe', 'pipe'], detached: grupo,
158
+ });
159
+ let saida = '', erro = '', bytes = 0, terminado = false;
160
+ const concluir = (r) => {
161
+ if (terminado)
162
+ return;
163
+ terminado = true;
164
+ clearTimeout(prazo);
165
+ opcoesDoWorker.signal?.removeEventListener('abort', cancelar);
166
+ resolve(r);
167
+ };
168
+ const interromper = (motivo) => {
169
+ if (terminado)
170
+ return;
171
+ filho.stdout?.destroy();
172
+ filho.stderr?.destroy();
173
+ try {
174
+ if (filho.pid)
175
+ process.kill(grupo ? -filho.pid : filho.pid, 'SIGKILL');
176
+ }
177
+ catch { /* ja terminou */ }
178
+ concluir({ codigo: null, saida: '', erro, interrompido: motivo, pid: filho.pid ?? null });
179
+ };
180
+ const cancelar = () => interromper('cancelada');
181
+ const prazoMs = opcoesDoWorker.prazoMs ?? exports.PRAZO_DA_CONSULTA_MS;
182
+ const prazo = setTimeout(() => interromper(`prazo de ${prazoMs} ms`), prazoMs);
183
+ opcoesDoWorker.signal?.addEventListener('abort', cancelar, { once: true });
184
+ filho.stdout.setEncoding('utf8');
185
+ filho.stdout.on('data', (parte) => {
186
+ saida += parte;
187
+ bytes += Buffer.byteLength(parte);
188
+ if (bytes > TETO_DO_STDOUT)
189
+ interromper('saida acima do teto do worker');
190
+ });
191
+ filho.stderr.setEncoding('utf8');
192
+ filho.stderr.on('data', (parte) => { if (erro.length < 4096)
193
+ erro += parte; });
194
+ filho.stdin.on('error', () => undefined);
195
+ filho.on('error', (e) => concluir({ codigo: null, saida: '', erro: e.message, interrompido: null, pid: filho.pid ?? null }));
196
+ filho.on('close', (codigo, sinal) => concluir({ codigo: sinal ? null : codigo, saida, erro, interrompido: null, pid: filho.pid ?? null }));
197
+ filho.stdin.end(JSON.stringify({ raiz, argv }));
198
+ try {
199
+ if (filho.pid)
200
+ opcoesDoWorker.aoIniciar?.(filho.pid);
201
+ }
202
+ catch { /* gancho de quem testa: nao muda o resultado da consulta */ }
203
+ });
204
+ }
205
+ const texto = (t, isError = false) => ({ content: [{ type: 'text', text: t }], ...(isError ? { isError: true } : {}) });
206
+ /**
207
+ * As quatro tools, chamadas pelo servidor so com a flag ligada no startup. `carregar` e `thread` sao os
208
+ * do servidor: o projeto fixado e a thread conferida (escopo da sessao filha e worktree confinada).
209
+ */
210
+ function registrarConsultasDoGrafo(registrar, contexto) {
211
+ // CHECK rodada 2 (N4): o confinamento compara caminhos reais, tambem quando quem registra passa outra forma da raiz.
212
+ const raiz = fs.realpathSync(contexto.raiz);
213
+ for (const d of DEFINICOES) {
214
+ registrar(d.nome, { description: d.descricao, inputSchema: d.schema, annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false } }, async (args, extra) => {
215
+ if (!grafoLigado(contexto.carregar().manifesto)) {
216
+ 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');
217
+ }
218
+ const t = contexto.thread(args.threadId);
219
+ // CHECK rodada 1 (S6): o caminho real que o worker recebe fica dentro do projeto, conferido aqui de novo.
220
+ const cwd = fs.realpathSync(t.worktree ?? raiz);
221
+ if (cwd !== raiz && !cwd.startsWith(raiz + path.sep))
222
+ throw Error('mcp.scope.violation: worktree fora do projeto');
223
+ const r = await consultarPeloWorker(cwd, d.argv(args), { signal: extra.signal, prazoMs: contexto.prazoMs });
224
+ if (r.interrompido)
225
+ throw Error(`grafo.mcp.indisponivel: consulta interrompida (${r.interrompido})`);
226
+ if (r.codigo === 0 && Buffer.byteLength(r.saida) <= (args.tetoBytes ?? exports.TETO_PADRAO))
227
+ return texto(r.saida);
228
+ if (r.codigo === 1 && r.saida.startsWith('{'))
229
+ return texto(r.saida, true);
230
+ throw Error(`grafo.mcp.indisponivel: o worker saiu com ${r.codigo ?? 'sinal'}${r.erro ? `: ${r.erro.split('\n')[0].slice(0, 300)}` : ''}`);
231
+ });
232
+ }
233
+ }
@@ -47,7 +47,9 @@ const node_util_1 = require("node:util");
47
47
  const smol_toml_1 = require("smol-toml");
48
48
  const manifest_1 = require("./manifest");
49
49
  exports.TOOLS_DONO_CODEX = ['ork_thread_new', 'ork_phase_run', 'ork_request_decision'];
50
- exports.TOOLS_FILHO_CODEX = ['ork_artifact_write', 'ork_claim_add', 'ork_git_commit', 'ork_verify', 'ork_ship'];
50
+ // RM-037 (rm037defeito, defeito 1): a decisao autonoma da sessao entra com as outras mutacoes do filho;
51
+ // sem o grant, o codex com approval_policy never recusaria a chamada.
52
+ exports.TOOLS_FILHO_CODEX = ['ork_artifact_write', 'ork_claim_add', 'ork_decision_record', 'ork_git_commit', 'ork_verify', 'ork_ship'];
51
53
  // Brain mutation tools are deliberately excluded from the existing opt-in bundle.
52
54
  // A native integration must request exact startup grants plus scoped activation.
53
55
  var company_brain_mcp_1 = require("./company-brain-mcp");
@@ -198,8 +200,9 @@ function migrarEnvVarsCodex(bruto, entrada) {
198
200
  return bruto.slice(0, corpoFim) + (corpo.endsWith('\n') ? '' : '\n') + `env_vars = ${valor}\n` + bruto.slice(corpoFim);
199
201
  }
200
202
  function instalarMcp(opcoes) {
203
+ // Ensaio da 0.5.0: os READMEs dos plugins mandavam `--project .`, recusado sem dizer como acertar.
201
204
  if (!path.isAbsolute(opcoes.projeto))
202
- 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"');
203
206
  if (!['claude-code', 'codex'].includes(opcoes.host))
204
207
  throw Error('mcp.install.host.invalid');
205
208
  if (opcoes.transporteShip !== undefined && !['github-ssh', 'bare-local'].includes(opcoes.transporteShip))