@orkastery/cli 0.5.1 → 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.
Files changed (110) hide show
  1. package/adapters/claude-code/agents/ork-master.md +2 -0
  2. package/adapters/claude-code/commands/master.md +8 -0
  3. package/adapters/claude-code/commands/ork.md +9 -0
  4. package/adapters/codex/skills/ork/SKILL.md +18 -3
  5. package/adapters/hermes/README.md +1 -0
  6. package/adapters/hermes/bin/ork-network-status.sh +10 -0
  7. package/adapters/hermes/hermes.plugin.json +1 -0
  8. package/adapters/hermes/hitl-ingress/__init__.py +2 -2
  9. package/adapters/hermes/skills/orkastery-devmaster/SKILL.md +6 -5
  10. package/adapters/openclaw/README.md +24 -4
  11. package/adapters/openclaw/dist/hitl-ingress.js +2 -2
  12. package/adapters/openclaw/dist/index.js +46 -11
  13. package/adapters/openclaw/openclaw.plugin.json +5 -1
  14. package/adapters/openclaw/package.json +2 -2
  15. package/adapters/openclaw/src/hitl-ingress.ts +2 -2
  16. package/adapters/openclaw/src/index.ts +49 -11
  17. package/assets/orkmind_bridge.py +117 -9
  18. package/dist/adapters/claude-bg.js +39 -5
  19. package/dist/adapters/codex.js +4 -3
  20. package/dist/agents-md.js +1 -0
  21. package/dist/auditrun.js +5 -1
  22. package/dist/board.js +16 -14
  23. package/dist/branch-de-estado.js +66 -10
  24. package/dist/busca-semantica.js +52 -12
  25. package/dist/canarios-rm057.js +149 -0
  26. package/dist/canarios.js +2 -0
  27. package/dist/ci.js +8 -1
  28. package/dist/claims.js +39 -1
  29. package/dist/company-brain-context.js +158 -22
  30. package/dist/docs.js +48 -9
  31. package/dist/doctor-pulse-cron.js +194 -0
  32. package/dist/doctor-rede.js +149 -0
  33. package/dist/doctor.js +89 -9
  34. package/dist/entrega-pr.js +20 -8
  35. package/dist/esgotamentos.js +181 -0
  36. package/dist/experiencia.js +12 -2
  37. package/dist/fabrica-estado.js +14 -6
  38. package/dist/fabrica-publicar.js +6 -0
  39. package/dist/fechamento.js +20 -66
  40. package/dist/forja.js +31 -7
  41. package/dist/gates.js +5 -1
  42. package/dist/hitl-contract.js +59 -4
  43. package/dist/hitl-curto.js +11 -6
  44. package/dist/hitl-gates.js +5 -0
  45. package/dist/hitl-lote.js +5 -2
  46. package/dist/hitl-resumo.js +9 -0
  47. package/dist/hitl-tempo-parado.js +201 -0
  48. package/dist/hitl-texto-livre.js +5 -5
  49. package/dist/horario.js +27 -0
  50. package/dist/hosts.js +52 -10
  51. package/dist/impedimento.js +205 -0
  52. package/dist/index.js +391 -74
  53. package/dist/indice-vetorial.js +87 -10
  54. package/dist/indice.js +3 -1
  55. package/dist/init.js +17 -0
  56. package/dist/integracoes-locais.js +2 -0
  57. package/dist/intelligence-graph-cli.js +91 -8
  58. package/dist/intelligence-graph-contexto.js +215 -0
  59. package/dist/intelligence-graph-parsers.js +6 -4
  60. package/dist/leases.js +664 -87
  61. package/dist/ledger-stats.js +15 -1
  62. package/dist/ledger.js +3 -0
  63. package/dist/licoes.js +1 -0
  64. package/dist/maestro-runtime.js +4 -1
  65. package/dist/manifest.js +20 -1
  66. package/dist/master-nota.js +14 -5
  67. package/dist/master.js +129 -12
  68. package/dist/mcp-artifacts.js +34 -3
  69. package/dist/mcp-git.js +20 -3
  70. package/dist/mcp-grafo-worker.js +5 -3
  71. package/dist/mcp-grafo.js +50 -4
  72. package/dist/mcp-server.js +14 -1
  73. package/dist/memoria.js +48 -6
  74. package/dist/network-roadmap.js +191 -16
  75. package/dist/ocupacao.js +11 -3
  76. package/dist/orkmind.js +90 -5
  77. package/dist/orquestracao.js +12 -10
  78. package/dist/parado-no-condutor.js +188 -32
  79. package/dist/phase.js +68 -15
  80. package/dist/policies.js +52 -1
  81. package/dist/portfolio.js +4 -1
  82. package/dist/projeto-alvo.js +5 -1
  83. package/dist/prompts.js +36 -1
  84. package/dist/prova-ativacao.js +75 -6
  85. package/dist/pulse-delivery.js +34 -2
  86. package/dist/pulse.js +49 -3
  87. package/dist/ratelimit.js +20 -4
  88. package/dist/recall.js +3 -1
  89. package/dist/rede-adesao.js +399 -0
  90. package/dist/rede-forja.js +289 -0
  91. package/dist/rede-projetos.js +239 -0
  92. package/dist/rede-status.js +336 -0
  93. package/dist/rede.js +980 -0
  94. package/dist/retry.js +69 -2
  95. package/dist/roadmap-status.js +23 -3
  96. package/dist/runtime-profiles.js +22 -1
  97. package/dist/saida-segura.js +58 -0
  98. package/dist/session-watcher-claude.js +113 -23
  99. package/dist/sessoes-contas.js +187 -0
  100. package/dist/sessoes-inventario.js +6 -18
  101. package/dist/sessoes.js +83 -2
  102. package/dist/ship.js +85 -19
  103. package/dist/thread.js +192 -13
  104. package/dist/worktree.js +5 -2
  105. package/eval/fixtures/fx-pedido-colado/caso.json +20 -0
  106. package/package.json +7 -3
  107. package/references/definition-of-done.md +1 -1
  108. package/skills/README.md +1 -1
  109. package/skills/core/orkastery-bootstrap/SKILL.md +2 -2
  110. package/skills/phases/master-metrics/SKILL.md +8 -3
@@ -0,0 +1,194 @@
1
+ "use strict";
2
+ /**
3
+ * RM-039 (B6): o `ork doctor` acusa o pulse que bate devagar demais.
4
+ *
5
+ * A cadencia por tag (#OrkPulseOn-15m e as outras) so vale inteira com a varredura do cron de 15
6
+ * em 15 minutos. A instalacao antiga fica em `0 * * * *`, e nada avisava. Com o transporte do
7
+ * pulse configurado (`.orkastery/monitor/pulse-host.json`), o doctor le o `crontab -l` e avisa,
8
+ * sem bloquear, quando a linha da varredura falta ou bate mais devagar que o template
9
+ * `monitor/pulse.cron`. O doctor nunca edita o crontab: a correcao traz a linha pronta.
10
+ */
11
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
12
+ if (k2 === undefined) k2 = k;
13
+ var desc = Object.getOwnPropertyDescriptor(m, k);
14
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
15
+ desc = { enumerable: true, get: function() { return m[k]; } };
16
+ }
17
+ Object.defineProperty(o, k2, desc);
18
+ }) : (function(o, m, k, k2) {
19
+ if (k2 === undefined) k2 = k;
20
+ o[k2] = m[k];
21
+ }));
22
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
23
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
24
+ }) : function(o, v) {
25
+ o["default"] = v;
26
+ });
27
+ var __importStar = (this && this.__importStar) || (function () {
28
+ var ownKeys = function(o) {
29
+ ownKeys = Object.getOwnPropertyNames || function (o) {
30
+ var ar = [];
31
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
32
+ return ar;
33
+ };
34
+ return ownKeys(o);
35
+ };
36
+ return function (mod) {
37
+ if (mod && mod.__esModule) return mod;
38
+ var result = {};
39
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
40
+ __setModuleDefault(result, mod);
41
+ return result;
42
+ };
43
+ })();
44
+ Object.defineProperty(exports, "__esModule", { value: true });
45
+ exports.COMANDO_DO_TEMPLATE = exports.SCRIPT_DA_VARREDURA = exports.BATIDA_DO_TEMPLATE = void 0;
46
+ exports.lerCrontabDoSistema = lerCrontabDoSistema;
47
+ exports.intervaloEmMinutos = intervaloEmMinutos;
48
+ exports.linhasDaVarredura = linhasDaVarredura;
49
+ exports.scriptDaVarreduraDoOrk = scriptDaVarreduraDoOrk;
50
+ exports.comandoDaVarredura = comandoDaVarredura;
51
+ exports.checarCronDoPulse = checarCronDoPulse;
52
+ const fs = __importStar(require("node:fs"));
53
+ const path = __importStar(require("node:path"));
54
+ const util_1 = require("./util");
55
+ /** A batida do template `monitor/pulse.cron` (I-50): a menor cadencia das tags. */
56
+ exports.BATIDA_DO_TEMPLATE = '*/15 * * * *';
57
+ /** O script que o template agenda; a linha do crontab e achada por ele. */
58
+ exports.SCRIPT_DA_VARREDURA = 'varredura-pulse.sh';
59
+ /** Comando do template antes da troca do caminho pelo checkout de quem instala. */
60
+ exports.COMANDO_DO_TEMPLATE = '/CAMINHO/orkastery/monitor/varredura-pulse.sh';
61
+ /** Le o crontab do usuario do processo, sem shell e com prazo. */
62
+ function lerCrontabDoSistema() {
63
+ const r = (0, util_1.exec)('crontab', ['-l'], undefined, 5000);
64
+ if (r.ok)
65
+ return { ok: true, texto: r.stdout };
66
+ if (r.error === 'ENOENT')
67
+ return { ok: false, motivo: 'comando crontab ausente nesta maquina' };
68
+ const msg = (r.stderr || r.stdout).trim().split('\n')[0] ?? '';
69
+ return { ok: false, motivo: msg ? `crontab -l falhou: ${msg}` : `crontab -l saiu ${r.code}` };
70
+ }
71
+ const MACROS = { '@hourly': 60, '@daily': 1440, '@midnight': 1440, '@weekly': 10080,
72
+ '@monthly': 43200, '@yearly': 525600, '@annually': 525600 };
73
+ /**
74
+ * Maior intervalo, em minutos, entre duas batidas da linha (so os campos de minuto e hora; dia,
75
+ * mes e dia da semana restritos contam como mais devagar que uma hora). `null` quando a expressao
76
+ * nao e reconhecida: o doctor entao avisa pela linha, sem adivinhar.
77
+ */
78
+ function intervaloEmMinutos(expressao) {
79
+ const campos = expressao.trim().split(/\s+/);
80
+ if (campos.length === 1 && campos[0].startsWith('@'))
81
+ return MACROS[campos[0]] ?? null;
82
+ if (campos.length !== 5)
83
+ return null;
84
+ const [minuto, hora, dia, mes, semana] = campos;
85
+ const minutos = valoresDoCampo(minuto, 0, 59);
86
+ if (!minutos)
87
+ return null;
88
+ if (hora !== '*' || dia !== '*' || mes !== '*' || semana !== '*')
89
+ return Math.max(60, maiorLacuna(minutos, 60));
90
+ return maiorLacuna(minutos, 60);
91
+ }
92
+ function valoresDoCampo(campo, min, max) {
93
+ const valores = new Set();
94
+ for (const parte of campo.split(',')) {
95
+ const m = /^(\*|(\d+)(?:-(\d+))?)(?:\/(\d+))?$/.exec(parte);
96
+ if (!m)
97
+ return null;
98
+ const inicio = m[1] === '*' ? min : Number(m[2]);
99
+ const fim = m[1] === '*' ? max : m[3] !== undefined ? Number(m[3]) : m[4] !== undefined ? max : inicio;
100
+ const passo = m[4] !== undefined ? Number(m[4]) : 1;
101
+ if (passo < 1 || inicio < min || fim > max || inicio > fim)
102
+ return null;
103
+ for (let v = inicio; v <= fim; v += passo)
104
+ valores.add(v);
105
+ }
106
+ return [...valores].sort((a, b) => a - b);
107
+ }
108
+ /** Maior distancia entre dois valores consecutivos, contando a volta do ciclo. */
109
+ function maiorLacuna(valores, ciclo) {
110
+ let maior = 0;
111
+ for (let i = 0; i < valores.length; i++) {
112
+ const proximo = i + 1 < valores.length ? valores[i + 1] : valores[0] + ciclo;
113
+ maior = Math.max(maior, proximo - valores[i]);
114
+ }
115
+ return maior;
116
+ }
117
+ /** Separa a expressao de horario do comando numa linha do crontab. */
118
+ function dividirLinha(linha) {
119
+ const t = linha.trim();
120
+ if (t.startsWith('@')) {
121
+ const m = /^(@\S+)\s+(.+)$/.exec(t);
122
+ return m ? { quando: m[1], comando: m[2] } : null;
123
+ }
124
+ const m = /^(\S+\s+\S+\s+\S+\s+\S+\s+\S+)\s+(.+)$/.exec(t);
125
+ return m ? { quando: m[1], comando: m[2] } : null;
126
+ }
127
+ /** Linhas ativas (nao comentario, nao variavel) que agendam a varredura do pulse. */
128
+ function linhasDaVarredura(crontab) {
129
+ return crontab.split('\n').map(l => l.trim())
130
+ .filter(l => l && !l.startsWith('#') && !/^[A-Za-z_][A-Za-z0-9_]*\s*=/.test(l) && l.includes(exports.SCRIPT_DA_VARREDURA));
131
+ }
132
+ /** Aspas de shell so quando o caminho precisa (a linha vai colada no crontab, que roda por `sh`). */
133
+ function citar(caminho) {
134
+ return /^[A-Za-z0-9_./:@+-]+$/.test(caminho) ? caminho : `'${caminho.replace(/'/g, `'\\''`)}'`;
135
+ }
136
+ /**
137
+ * O script da varredura que este `ork` traz: no pacote do npm, `monitor/` fica ao lado de `dist/`; no
138
+ * checkout, na raiz do repositorio, dois niveis acima de `core/dist/`. `null` quando nenhum existe.
139
+ */
140
+ function scriptDaVarreduraDoOrk(dirDoDist = __dirname) {
141
+ for (const dir of [path.join(dirDoDist, '..', 'monitor'), path.join(dirDoDist, '..', '..', 'monitor')]) {
142
+ const script = path.join(dir, exports.SCRIPT_DA_VARREDURA);
143
+ if (fs.existsSync(script))
144
+ return path.resolve(script);
145
+ }
146
+ return null;
147
+ }
148
+ /**
149
+ * O comando que a linha nova do crontab roda. Ensaio de 03/10/2026 (RM-049): a correcao apontava
150
+ * `<projeto>/monitor/varredura-pulse.sh`, que so existe no checkout do proprio Orkastery; quem instalou
151
+ * pelo npm recebia um caminho que nao existe. Quando o projeto nao traz o script, a linha usa o do `ork`
152
+ * instalado e diz o projeto por `ORK_PULSE_PROJECT` (o cron nao roda no diretorio do projeto).
153
+ */
154
+ function comandoDaVarredura(raizDoProjeto, scriptDoOrk = scriptDaVarreduraDoOrk()) {
155
+ const doProjeto = path.join(raizDoProjeto, 'monitor', exports.SCRIPT_DA_VARREDURA);
156
+ if (fs.existsSync(doProjeto) || !scriptDoOrk)
157
+ return citar(doProjeto);
158
+ return `ORK_PULSE_PROJECT=${citar(raizDoProjeto)} ${citar(scriptDoOrk)}`;
159
+ }
160
+ /**
161
+ * Check "cadencia do pulse no cron". `null` quando o projeto nao tem transporte do pulse: sem
162
+ * `pulse-host.json` nao ha resumo para entregar, e o doctor nao fala de cron.
163
+ */
164
+ function checarCronDoPulse(dirMonitor, raizDoCheckout, ler = lerCrontabDoSistema, scriptDoOrk = scriptDaVarreduraDoOrk()) {
165
+ if (!fs.existsSync(path.join(dirMonitor, 'pulse-host.json')))
166
+ return null;
167
+ const nome = 'cadencia do pulse no cron';
168
+ const comandoSugerido = comandoDaVarredura(raizDoCheckout, scriptDoOrk);
169
+ const linhaNova = (comando) => `${exports.BATIDA_DO_TEMPLATE} ${comando}`;
170
+ const ausente = (detalhe) => ({ nome, nivel: 'warn', detalhe,
171
+ correcao: `adicione ao crontab (crontab -e), como no template monitor/pulse.cron: ${linhaNova(comandoSugerido)}` });
172
+ const leitura = ler();
173
+ if (!leitura.ok)
174
+ return ausente(`pulse-host.json presente, mas sem crontab para ler (${leitura.motivo}): a varredura nao bate`);
175
+ const linhas = linhasDaVarredura(leitura.texto);
176
+ if (linhas.length === 0)
177
+ return ausente(`pulse-host.json presente, e nenhuma linha do crontab roda ${exports.SCRIPT_DA_VARREDURA}: o resumo nao sai`);
178
+ const lentas = [];
179
+ for (const linha of linhas) {
180
+ const partes = dividirLinha(linha);
181
+ const intervalo = partes ? intervaloEmMinutos(partes.quando) : null;
182
+ if (intervalo === null || intervalo > 15)
183
+ lentas.push({ linha, comando: partes?.comando ?? comandoSugerido, intervalo });
184
+ }
185
+ if (lentas.length < linhas.length) {
186
+ return { nome, nivel: 'ok', detalhe: `varredura no cron a cada 15 minutos ou menos (${linhas.length} linha(s))` };
187
+ }
188
+ const l = lentas[0];
189
+ const ritmo = l.intervalo === null ? 'com horario que o doctor nao le' : l.intervalo === 60 ? 'de hora em hora'
190
+ : `a cada ${l.intervalo} minutos`;
191
+ return { nome, nivel: 'warn',
192
+ detalhe: `a varredura do pulse bate ${ritmo} (\`${l.linha}\`): a cadencia por tag (RM-039) pede 15 minutos`,
193
+ correcao: `troque a linha no crontab (crontab -e) por: ${linhaNova(l.comando)}` };
194
+ }
@@ -0,0 +1,149 @@
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.CAUDA_DO_LOG_BYTES = void 0;
37
+ exports.caudaDoLogDaRede = caudaDoLogDaRede;
38
+ exports.correcaoDaFalha = correcaoDaFalha;
39
+ exports.checarRede = checarRede;
40
+ /**
41
+ * RM-053 (fatia 2): o check "rede" do `ork doctor`.
42
+ *
43
+ * A rede pessoal publica sozinha (batida do pulse e eventos de thread) e so deixava rastro em
44
+ * `~/.orkastery/rede/rede.log`: a srvjcp86 ficou de 02/10 em diante como membro herdado da fabrica,
45
+ * com toda publicacao caindo em `rede.sem-repositorio`, e o doctor dizia PRONTO sem uma palavra.
46
+ *
47
+ * O check diz a adesao, a casa, a ultima batida publicada e a ultima falha do log. Ele so le
48
+ * arquivos locais (`rede.json`, `maquina.json`, `rede/publicada.json`, `rede/rede.log`): nao chama
49
+ * a forja, nao cria estado e roda de qualquer diretorio. Nunca reprova: a rede e opcional e o
50
+ * doctor e gate de despacho; o pior nivel e `warn`, com a correcao.
51
+ */
52
+ const fs = __importStar(require("node:fs"));
53
+ const path = __importStar(require("node:path"));
54
+ const horario_1 = require("./horario");
55
+ const rede_1 = require("./rede");
56
+ const rede_adesao_1 = require("./rede-adesao");
57
+ const rede_status_1 = require("./rede-status");
58
+ const saida_segura_1 = require("./saida-segura");
59
+ /** O fim do `rede.log` que o doctor le: o log so cresce, e o que importa e o mais recente. */
60
+ exports.CAUDA_DO_LOG_BYTES = 64 * 1024;
61
+ /** As linhas legiveis do fim do `rede.log`, da mais antiga para a mais nova. Sem log, lista vazia. */
62
+ function caudaDoLogDaRede(arquivo = path.join((0, rede_adesao_1.pastaDaRede)(), 'rede.log')) {
63
+ let texto;
64
+ try {
65
+ const fd = fs.openSync(arquivo, 'r');
66
+ try {
67
+ const tamanho = fs.fstatSync(fd).size;
68
+ const inicio = Math.max(0, tamanho - exports.CAUDA_DO_LOG_BYTES);
69
+ const buf = Buffer.alloc(tamanho - inicio);
70
+ fs.readSync(fd, buf, 0, buf.length, inicio);
71
+ texto = buf.toString('utf8');
72
+ // A primeira linha de uma cauda cortada vem pela metade.
73
+ if (inicio > 0)
74
+ texto = texto.slice(texto.indexOf('\n') + 1);
75
+ }
76
+ finally {
77
+ fs.closeSync(fd);
78
+ }
79
+ }
80
+ catch {
81
+ return [];
82
+ }
83
+ const registros = [];
84
+ for (const linha of texto.split('\n')) {
85
+ if (!linha.trim())
86
+ continue;
87
+ try {
88
+ const r = JSON.parse(linha);
89
+ if (typeof r.ts !== 'string' || !Number.isFinite(Date.parse(r.ts)) || typeof r.acao !== 'string')
90
+ continue;
91
+ registros.push({ ts: r.ts, acao: r.acao, origem: typeof r.origem === 'string' ? r.origem : null,
92
+ erro: typeof r.erro === 'string' ? r.erro : null });
93
+ }
94
+ catch { /* linha ilegivel: o log e informativo */ }
95
+ }
96
+ return registros;
97
+ }
98
+ const curto = (s, teto = 200) => {
99
+ const limpo = (0, saida_segura_1.emUmaLinha)(s).trim();
100
+ return limpo.length > teto ? `${limpo.slice(0, teto - 3)}...` : limpo;
101
+ };
102
+ /** A correcao pela familia do erro da ultima falha (o prefixo tipado, como `rede.sem-repositorio`). */
103
+ function correcaoDaFalha(erro) {
104
+ const codigo = /^([a-z]+\.[a-z-]+):/.exec(erro)?.[1] ?? '';
105
+ if (codigo === 'rede.sem-repositorio' || codigo === 'rede.repositorio-alheio') {
106
+ return 'a casa da rede ainda nao existe: o dono roda ork network entrar, que cria o repositorio privado; ou ork network sair, para tirar esta maquina da rede';
107
+ }
108
+ if (codigo === 'rede.sem-forja')
109
+ return 'faca login na forja (gh auth login ou glab auth login) e rode ork network publicar';
110
+ if (codigo === 'rede.repositorio-publico')
111
+ return 'torne a casa da rede privada na forja; nada e publicado num repositorio publico';
112
+ if (codigo === 'rede.nome-em-uso')
113
+ return 'escolha outro nome com ork network entrar --maquina NOME, ou tome este com --forcar';
114
+ return 'ork network status mostra as lacunas; ork network publicar tenta de novo';
115
+ }
116
+ /** O check "rede". `agoraMs` e o log ficam injetaveis para o teste. */
117
+ function checarRede(opcoes = {}) {
118
+ const nome = 'rede';
119
+ const agora = opcoes.agoraMs ?? Date.now();
120
+ const adesao = (0, rede_adesao_1.adesaoDaRede)();
121
+ if (!adesao.membro) {
122
+ return { nome, nivel: 'ok', detalhe: adesao.config && !adesao.config.membro
123
+ ? 'fora da rede (saiu com ork network sair)'
124
+ : 'fora da rede; ork network entrar poe esta maquina na rede da pessoa' };
125
+ }
126
+ const marca = (0, rede_1.lerMarcaDaRede)();
127
+ const config = adesao.config;
128
+ const casa = config?.host && config.dono && config.repositorio ? `${config.host}/${config.dono}/${config.repositorio}`
129
+ : typeof marca?.casa === 'string' ? marca.casa : null;
130
+ const registros = caudaDoLogDaRede(opcoes.log);
131
+ const falha = [...registros].reverse().find((r) => r.acao === 'falhou') ?? null;
132
+ const emBatida = marca && typeof marca.em === 'string' && Number.isFinite(Date.parse(marca.em)) ? marca.em : null;
133
+ const idade = emBatida ? agora - Date.parse(emBatida) : NaN;
134
+ const partes = [
135
+ adesao.adesao === 'fabrica' ? 'membro herdado da fabrica' : 'membro',
136
+ casa ? `casa ${curto(casa, 120)}` : 'casa ainda nao gravada',
137
+ emBatida ? `ultima batida ${(0, horario_1.formatarDataHora)(emBatida)} (ha ${(0, rede_status_1.duracao)(idade)})` : 'nenhuma batida publicada',
138
+ falha ? `ultima falha ${(0, horario_1.formatarDataHora)(falha.ts)}${falha.origem ? ` (${curto(falha.origem, 20)})` : ''}: ${curto(falha.erro ?? 'sem detalhe')}`
139
+ : 'nenhuma falha no rede.log',
140
+ ];
141
+ // A falha mais nova que a ultima batida e o estado de agora; a de antes ja passou.
142
+ const falhaAtual = !!falha && (!emBatida || Date.parse(falha.ts) > Date.parse(emBatida));
143
+ const parada = !emBatida || !(idade <= rede_status_1.SEM_BATIDA_MS);
144
+ if (!falhaAtual && !parada)
145
+ return { nome, nivel: 'ok', detalhe: partes.join('; ') };
146
+ const correcao = falhaAtual ? correcaoDaFalha(falha.erro ?? '')
147
+ : 'a batida sai do pulse: confira o cron do pulse e rode ork network publicar';
148
+ return { nome, nivel: 'warn', detalhe: partes.join('; '), correcao };
149
+ }
package/dist/doctor.js CHANGED
@@ -40,6 +40,7 @@ var __importStar = (this && this.__importStar) || (function () {
40
40
  })();
41
41
  Object.defineProperty(exports, "__esModule", { value: true });
42
42
  exports.TETO_DO_DONO_DO_GIT = void 0;
43
+ exports.manifestoForaDoRepositorio = manifestoForaDoRepositorio;
43
44
  exports.checarContas = checarContas;
44
45
  exports.checarChaveDeEmbedding = checarChaveDeEmbedding;
45
46
  exports.checarDonoDoGit = checarDonoDoGit;
@@ -69,6 +70,41 @@ const runtime_ambiente_1 = require("./runtime-ambiente");
69
70
  const runtime_profiles_1 = require("./runtime-profiles");
70
71
  const preflight_1 = require("./preflight");
71
72
  const setup_1 = require("./setup");
73
+ const doctor_pulse_cron_1 = require("./doctor-pulse-cron");
74
+ const doctor_rede_1 = require("./doctor-rede");
75
+ /**
76
+ * Ensaio de 03/10/2026 (RM-049): o manifesto e achado subindo a partir do diretorio atual. Um
77
+ * `orkastery.yaml` de uma pasta acima do repositorio (um `ork init` rodado por engano no HOME) era
78
+ * lido como o deste projeto, com outro nome e outra abbrev, e o doctor dizia PRONTO. Vale o manifesto
79
+ * dentro do repositorio ou na arvore principal dele (a worktree de thread le o da arvore principal).
80
+ */
81
+ function manifestoForaDoRepositorio(dirInicial, caminhoDoManifesto) {
82
+ const topo = (0, util_1.exec)('git', ['rev-parse', '--show-toplevel'], dirInicial);
83
+ if (!topo.ok)
84
+ return null;
85
+ const real = (p) => { try {
86
+ return fs.realpathSync(p);
87
+ }
88
+ catch {
89
+ return path.resolve(p);
90
+ } };
91
+ const dirDoManifesto = real(path.dirname(caminhoDoManifesto));
92
+ const dentro = (raiz) => {
93
+ const r = path.relative(real(raiz), dirDoManifesto);
94
+ return r === '' || (!r.startsWith('..') && !path.isAbsolute(r));
95
+ };
96
+ if (dentro(topo.stdout.trim()))
97
+ return null;
98
+ const comum = (0, util_1.exec)('git', ['rev-parse', '--path-format=absolute', '--git-common-dir'], dirInicial);
99
+ if (comum.ok && path.basename(comum.stdout.trim()) === '.git' && dentro(path.dirname(comum.stdout.trim())))
100
+ return null;
101
+ return {
102
+ nome: 'manifesto do repositorio',
103
+ nivel: 'warn',
104
+ detalhe: `o ${manifest_1.NOME_MANIFESTO} lido fica fora deste repositorio (${caminhoDoManifesto}): o ork conduz o projeto daquela pasta, nao o de ${topo.stdout.trim()}`,
105
+ correcao: `rode ork init na raiz deste repositorio (${topo.stdout.trim()}); se o manifesto de fora nasceu por engano, apague-o junto com o AGENTS.md e o .orkastery/ ao lado dele`,
106
+ };
107
+ }
72
108
  /**
73
109
  * I-33 (D7): check "contas por runtime". Cada perfil ativo tem o login conferido pelo proprio
74
110
  * CLI com o env dele (`claude auth status`, `codex login status`); o doctor so le e relata, sem
@@ -294,15 +330,40 @@ function textoDosBlocos(blocos) {
294
330
  porModo.set(b.tag, [...(porModo.get(b.tag) ?? []), b.bloco]);
295
331
  return [...porModo].map(([tag, numeros]) => `${tag} ${numeros.join(', ')}`).join('; ');
296
332
  }
333
+ /**
334
+ * Ensaio de 03/10 (R1): com perfil ativo do runtime, quem confere o login e o check "contas por runtime";
335
+ * sem perfil (ou com o store ilegivel, que aquele check reprova), o despacho usa o login do `claude` do processo.
336
+ */
337
+ function temPerfilAtivo(carregado, runtime) {
338
+ if (!carregado || carregado.erros.length)
339
+ return false;
340
+ try {
341
+ return (0, runtime_profiles_1.lerPerfisComContas)(carregado.raiz).perfis.some(p => p.runtime === runtime && p.estado !== 'desativado');
342
+ }
343
+ catch {
344
+ return false;
345
+ }
346
+ }
297
347
  /**
298
348
  * Fatia 2 do ensaio da 0.5.0 (P1): o `claude` so e obrigatorio quando algum bloco de modo permitido
299
349
  * despacha por ele. Sem manifesto, com manifesto invalido ou com setup ilegivel vale o padrao (todo
300
350
  * bloco no claude-bg), e a falta reprova como antes.
301
351
  */
302
- function checarRuntimeClaude(carregado, claude, versao) {
352
+ function checarRuntimeClaude(carregado, claude, versao, auth = null) {
303
353
  const nome = 'runtime claude-bg';
304
- if (claude)
305
- return { nome, nivel: 'ok', detalhe: `${claude} (${versao ?? 'versao desconhecida'})` };
354
+ if (claude) {
355
+ const detalhe = `${claude} (${versao ?? 'versao desconhecida'})`;
356
+ // Ensaio de 03/10 (R1): sem perfil de conta, o despacho usa o login do proprio `claude`; sem ele, o
357
+ // primeiro despacho falha. Conferencia inconclusiva (timeout, JSON ilegivel) nao prova falta de login.
358
+ if (auth && !auth.ok && !auth.transitorio) {
359
+ return { nome, nivel: 'warn', detalhe: `${detalhe}; ${auth.detalhe}: o despacho pelo claude-bg falharia`,
360
+ correcao: auth.pago
361
+ ? 'faca o login de assinatura (claude.ai) no `claude`, sem API key nem provider de nuvem'
362
+ : 'rode `claude` uma vez, faca o login de assinatura (/login) e aceite a confianca no diretorio do projeto; ' +
363
+ 'ou crie um perfil com ork accounts add <id> --runtime claude-bg --dir <pasta>' };
364
+ }
365
+ return { nome, nivel: 'ok', detalhe };
366
+ }
306
367
  const blocos = carregado && carregado.erros.length === 0 ? blocosDoRuntime(carregado, 'claude-bg') : null;
307
368
  if (blocos && blocos.principal.length === 0) {
308
369
  // CHECK, rodada 1 (S6): as fases nao precisam dele, mas o `ork audit run` despacha sempre pelo claude-bg.
@@ -351,8 +412,12 @@ function checarDespachoPeloCodex(carregado, codex, sonda) {
351
412
  : undefined,
352
413
  };
353
414
  }
354
- /** Roda todos os checks a partir do diretorio informado. */
355
- function checar(dirInicial = process.cwd(), nomesHerdados = (0, runtime_ambiente_1.nomesDeProviderAtivos)()) {
415
+ /**
416
+ * Roda todos os checks a partir do diretorio informado. RM-031: `analisadores` e o check do grafo
417
+ * (`checarAnalisadoresDoGrafo` do CLI do grafo), que o `index.ts` passa: fora da familia do grafo so
418
+ * ele e o worker do MCP a abrem (fronteira do KG1), e o doctor nao a importa.
419
+ */
420
+ function checar(dirInicial = process.cwd(), nomesHerdados = (0, runtime_ambiente_1.nomesDeProviderAtivos)(), lerCrontab = doctor_pulse_cron_1.lerCrontabDoSistema, conferirAuthDoClaude = () => adapter.conferirAuth(null), analisadores) {
356
421
  const checks = [];
357
422
  const major = versaoNode();
358
423
  checks.push({
@@ -361,6 +426,8 @@ function checar(dirInicial = process.cwd(), nomesHerdados = (0, runtime_ambiente
361
426
  detalhe: `v${process.versions.node}`,
362
427
  correcao: major >= 20 ? undefined : 'instale Node 20 ou superior',
363
428
  });
429
+ if (analisadores)
430
+ checks.push(analisadores());
364
431
  const git = (0, util_1.noPath)('git');
365
432
  checks.push({
366
433
  nome: 'git',
@@ -390,7 +457,7 @@ function checar(dirInicial = process.cwd(), nomesHerdados = (0, runtime_ambiente
390
457
  const carregado = (0, manifest_1.carregarManifesto)(dirInicial);
391
458
  const claude = adapter.disponivel();
392
459
  const v = claude ? adapter.versao() : null;
393
- checks.push(checarRuntimeClaude(carregado, claude, v));
460
+ checks.push(checarRuntimeClaude(carregado, claude, v, claude && !temPerfilAtivo(carregado, 'claude-bg') ? conferirAuthDoClaude() : null));
394
461
  // Segundo runtime homologado. Ausente e `warn`, nao `fail`: o claude-bg segue sendo o
395
462
  // padrao, e um projeto que nunca pediu codex nao pode ficar bloqueado por ele.
396
463
  const codex = codexAdapter.disponivel();
@@ -433,7 +500,8 @@ function checar(dirInicial = process.cwd(), nomesHerdados = (0, runtime_ambiente
433
500
  nome: 'manifesto',
434
501
  nivel: 'fail',
435
502
  detalhe: `${manifest_1.NOME_MANIFESTO} nao encontrado a partir de ${dirInicial}`,
436
- correcao: 'ork init',
503
+ // Ensaio de 03/10/2026 (RM-049): fora de repositorio, o `ork init` recusa; a correcao diz onde roda-lo.
504
+ correcao: dentroDeRepo ? 'ork init' : 'entre no repositorio do projeto (ou crie um com git init e o primeiro commit) e rode ork init',
437
505
  });
438
506
  }
439
507
  else {
@@ -446,6 +514,11 @@ function checar(dirInicial = process.cwd(), nomesHerdados = (0, runtime_ambiente
446
514
  (carregado.avisos.length > 0 ? `; ${carregado.avisos.join('; ')}` : ''),
447
515
  correcao: carregado.erros.length > 0 ? 'corrija o manifesto e rode ork doctor de novo' : undefined,
448
516
  });
517
+ if (dentroDeRepo) {
518
+ const fora = manifestoForaDoRepositorio(dirInicial, carregado.caminho);
519
+ if (fora)
520
+ checks.push(fora);
521
+ }
449
522
  checks.push(...checarOnboarding(carregado));
450
523
  const abbrev = carregado.manifesto.project.abbrev;
451
524
  const checkAbbrev = (0, slug_1.validarAbbrev)(abbrev);
@@ -554,6 +627,11 @@ function checar(dirInicial = process.cwd(), nomesHerdados = (0, runtime_ambiente
554
627
  : `${dirEstadoProjeto} ausente`,
555
628
  correcao: fs.existsSync(dirEstadoProjeto) ? undefined : 'ork init cria o diretorio de estado',
556
629
  });
630
+ // RM-039 (B6): com o transporte do pulse configurado, a varredura tem de bater de 15 em 15
631
+ // minutos; a instalacao antiga em `0 * * * *` vira aviso com a linha nova. Nunca edita o crontab.
632
+ const cronDoPulse = (0, doctor_pulse_cron_1.checarCronDoPulse)(path.join(dirEstadoProjeto, 'monitor'), (0, estado_thread_1.raizDoEstado)(carregado.raiz), lerCrontab);
633
+ if (cronDoPulse)
634
+ checks.push(cronDoPulse);
557
635
  checks.push(checarContas(carregado.raiz));
558
636
  // I-33 (D7): umask e permissoes do estado, na mesma regra do sensor (sem bits 0o022).
559
637
  checks.push(...(0, preflight_1.sondasDeAmbiente)(carregado.raiz));
@@ -573,6 +651,8 @@ function checar(dirInicial = process.cwd(), nomesHerdados = (0, runtime_ambiente
573
651
  });
574
652
  }
575
653
  }
654
+ // RM-053 (fatia 2): a rede da pessoa e da maquina, nao do projeto; vale de qualquer diretorio.
655
+ checks.push((0, doctor_rede_1.checarRede)());
576
656
  return checks;
577
657
  }
578
658
  /** Texto do relatorio, com o resumo "o que vale nesta maquina agora". */
@@ -608,8 +688,8 @@ function relatorio(checks) {
608
688
  return linhas.join('\n');
609
689
  }
610
690
  /** Executa o doctor e devolve o codigo de saida (0 = pronto). */
611
- function doctor(dirInicial = process.cwd(), nomesHerdados = (0, runtime_ambiente_1.nomesDeProviderAtivos)()) {
612
- const checks = checar(dirInicial, nomesHerdados);
691
+ function doctor(dirInicial = process.cwd(), nomesHerdados = (0, runtime_ambiente_1.nomesDeProviderAtivos)(), analisadores) {
692
+ const checks = checar(dirInicial, nomesHerdados, undefined, undefined, analisadores);
613
693
  const falhas = checks.filter((c) => c.nivel === 'fail').length;
614
694
  return { texto: relatorio(checks), codigo: falhas > 0 ? 1 : 0 };
615
695
  }
@@ -16,7 +16,9 @@ exports.registrarEntregasPorPr = registrarEntregasPorPr;
16
16
  * remota (ancestralidade conferida contra a ponta que o `ls-remote` devolve) e o check do CI no
17
17
  * head do PR. Sem CI verde, nao registra: merge sem prova independente nao e entrega.
18
18
  */
19
+ const branch_de_estado_1 = require("./branch-de-estado");
19
20
  const ci_1 = require("./ci");
21
+ const docs_1 = require("./docs");
20
22
  const fabrica_publicar_1 = require("./fabrica-publicar");
21
23
  const ledger_1 = require("./ledger");
22
24
  const modos_1 = require("./modos");
@@ -49,7 +51,7 @@ function avancarParaShip(raiz, threadId) {
49
51
  (0, thread_1.gravarThread)(raiz, atualizada);
50
52
  }
51
53
  function resultado(thread, acao, motivo, extra = {}) {
52
- return { thread, acao, motivo, mergeSha: null, headSha: null, ci: null, ...extra };
54
+ return { thread, acao, motivo, mergeSha: null, headSha: null, ci: null, docsPendentes: [], ...extra };
53
55
  }
54
56
  /**
55
57
  * `ork ship registrar-pr <thread>`: grava o `ship_done` da entrega feita por PR. Idempotente:
@@ -57,22 +59,26 @@ function resultado(thread, acao, motivo, extra = {}) {
57
59
  */
58
60
  function registrarEntregaPorPr(carregado, threadId, opcoes = {}) {
59
61
  const raiz = carregado.raiz;
60
- const remoto = opcoes.remoto ?? 'origin';
62
+ // RM-047: o `--remoto` vem da linha de comando; so nome de remoto chega ao git, e depois do `--`.
63
+ const remoto = (0, branch_de_estado_1.exigirRemoto)(opcoes.remoto ?? 'origin', 'ship');
61
64
  const base = carregado.manifesto.worktree.base_branch;
62
65
  const thread = (0, thread_1.lerThread)(raiz, threadId);
63
66
  if (thread.status === 'fechada')
64
67
  return resultado(threadId, 'ja-registrada', 'thread ja fechada');
65
68
  if (opcoes.buscar !== false)
66
- (0, util_1.exec)('git', ['fetch', '-q', remoto, `+refs/heads/${base}:refs/remotes/${remoto}/${base}`], raiz);
67
- const merge = mergeDaEntrega(raiz, threadId, remoto, base);
68
- if (!merge)
69
+ (0, util_1.exec)('git', ['fetch', '-q', '--', remoto, `+refs/heads/${base}:refs/remotes/${remoto}/${base}`], raiz);
70
+ const encontrado = mergeDaEntrega(raiz, threadId, remoto, base);
71
+ if (!encontrado)
69
72
  return resultado(threadId, 'sem-merge', `nenhum merge ship(${threadId}) em ${remoto}/${base}`);
73
+ // RM-044 (A3): o condutor roda o registrar-pr logo depois do merge; e aqui que ele fica sabendo da pagina que
74
+ // vai deixar o push da base vermelho, lida da base remota que o fetch acabou de trazer.
75
+ const merge = { ...encontrado, docsPendentes: (0, docs_1.docsPendentesDoMerge)(raiz, threadId, `refs/remotes/${remoto}/${base}`) };
70
76
  const dir = (0, thread_1.dirThread)(raiz, threadId);
71
77
  const jaTem = (0, ledger_1.lerLedger)(dir).some((e) => e.tipo === ledger_1.TIPOS_DE_EVENTO.shipConcluido && e.mergeSha === merge.mergeSha);
72
78
  if (jaTem)
73
79
  return resultado(threadId, 'ja-registrada', 'esta entrega ja tem ship_done', merge);
74
80
  // A ponta do remoto AGORA, e o merge dentro dela: e o que torna o push provado, nao presumido.
75
- const remotoAgora = (0, util_1.exec)('git', ['ls-remote', remoto, `refs/heads/${base}`], raiz);
81
+ const remotoAgora = (0, util_1.exec)('git', ['ls-remote', '--', remoto, `refs/heads/${base}`], raiz);
76
82
  const ponta = remotoAgora.ok ? remotoAgora.stdout.split('\t')[0].trim() : '';
77
83
  if (!ponta || !(0, util_1.exec)('git', ['merge-base', '--is-ancestor', merge.mergeSha, ponta], raiz).ok) {
78
84
  return resultado(threadId, 'recusada', `o merge ${merge.mergeSha.slice(0, 7)} nao esta na ponta de ${remoto}/${base}`, merge);
@@ -90,6 +96,9 @@ function registrarEntregaPorPr(carregado, threadId, opcoes = {}) {
90
96
  para: base,
91
97
  shaDe: merge.headSha,
92
98
  mergeSha: merge.mergeSha,
99
+ // RM-037 (fatia 5, A1): a ponta que o ls-remote provou vai em campo proprio; o recibo do Maestro confere o push
100
+ // contra ela quando a base andou depois do merge.
101
+ pontaDaBase: ponta,
93
102
  jaIncorporado: true,
94
103
  remoto,
95
104
  shaRemoto: ponta,
@@ -189,6 +198,9 @@ function registrarEntregaExternaPorPr(carregado, threadId, opcoes) {
189
198
  url: pr.html_url ?? `https://github.com/${repo}/pull/${n}`,
190
199
  shaDe: headSha,
191
200
  mergeSha,
201
+ // RM-037 (fatia 5, A1; aviso da rodada 1 do CHECK): a ponta conferida vai em campo proprio, como no PR local; a
202
+ // comparacao aceita `ahead`, e o recibo do Maestro confere o push contra ela.
203
+ pontaDaBase: ponta,
192
204
  jaIncorporado: true,
193
205
  remoto: `github:${repo}`,
194
206
  shaRemoto: ponta,
@@ -207,9 +219,9 @@ function registrarEntregaExternaPorPr(carregado, threadId, opcoes) {
207
219
  }
208
220
  /** `ork ship registrar-pr --todas`: toda thread aberta que ja tem merge `ship(<thread>)` na base. */
209
221
  function registrarEntregasPorPr(carregado, opcoes = {}) {
210
- const remoto = opcoes.remoto ?? 'origin';
222
+ const remoto = (0, branch_de_estado_1.exigirRemoto)(opcoes.remoto ?? 'origin', 'ship');
211
223
  const base = carregado.manifesto.worktree.base_branch;
212
- (0, util_1.exec)('git', ['fetch', '-q', remoto, `+refs/heads/${base}:refs/remotes/${remoto}/${base}`], carregado.raiz);
224
+ (0, util_1.exec)('git', ['fetch', '-q', '--', remoto, `+refs/heads/${base}:refs/remotes/${remoto}/${base}`], carregado.raiz);
213
225
  const r = (0, thread_1.listarIds)(carregado.raiz)
214
226
  .filter((id) => (0, thread_1.lerThread)(carregado.raiz, id).status !== 'fechada')
215
227
  .filter((id) => mergeDaEntrega(carregado.raiz, id, remoto, base) !== null)