@orkastery/cli 0.5.3 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (502) hide show
  1. package/adapters/README.md +18 -0
  2. package/adapters/claude-code/.claude-plugin/plugin.json +1 -0
  3. package/adapters/claude-code/README.md +195 -5
  4. package/adapters/claude-code/agents/ork-check.md +1 -0
  5. package/adapters/claude-code/agents/ork-go.md +1 -0
  6. package/adapters/claude-code/agents/ork-goal.md +1 -0
  7. package/adapters/claude-code/agents/ork-master.md +1 -0
  8. package/adapters/claude-code/agents/ork-plan.md +1 -0
  9. package/adapters/claude-code/agents/ork-ship.md +1 -0
  10. package/adapters/claude-code/hooks/hooks.json +2 -1
  11. package/adapters/claude-code/hooks/ork-guard.js +15 -1
  12. package/adapters/claude-code/hooks/ork-resposta.js +91 -0
  13. package/adapters/claude-code/hooks/ork-sensor.js +18 -2
  14. package/adapters/claude-code/hooks/quota-signal.js +14 -0
  15. package/adapters/claude-code/mods/autoridade.json +56 -0
  16. package/adapters/claude-code/mods/ork-clima/.claude-plugin/plugin.json +14 -0
  17. package/adapters/claude-code/mods/ork-clima/hooks/contas.ts +283 -0
  18. package/adapters/claude-code/mods/ork-clima/hooks/hooks.json +3 -0
  19. package/adapters/claude-code/mods/ork-clima/hooks/previsao.ts +393 -0
  20. package/adapters/claude-code/mods/ork-clima/hooks/register.ts +205 -0
  21. package/adapters/claude-code/mods/ork-clima/orkastery-mod.json +5 -0
  22. package/adapters/claude-code/mods/ork-clima/types/index.d.ts +80 -0
  23. package/adapters/claude-code/mods/ork-freio/.claude-plugin/plugin.json +14 -0
  24. package/adapters/claude-code/mods/ork-freio/hooks/carga.ts +160 -0
  25. package/adapters/claude-code/mods/ork-freio/hooks/conferencia.ts +309 -0
  26. package/adapters/claude-code/mods/ork-freio/hooks/hooks.json +3 -0
  27. package/adapters/claude-code/mods/ork-freio/hooks/lote.ts +270 -0
  28. package/adapters/claude-code/mods/ork-freio/hooks/register.ts +285 -0
  29. package/adapters/claude-code/mods/ork-freio/orkastery-mod.json +5 -0
  30. package/adapters/claude-code/mods/ork-freio/types/index.d.ts +127 -0
  31. package/adapters/claude-code/mods/ork-mensageiro/.claude-plugin/plugin.json +14 -0
  32. package/adapters/claude-code/mods/ork-mensageiro/hooks/caixa.ts +215 -0
  33. package/adapters/claude-code/mods/ork-mensageiro/hooks/hooks.json +3 -0
  34. package/adapters/claude-code/mods/ork-mensageiro/hooks/painel.tsx +165 -0
  35. package/adapters/claude-code/mods/ork-mensageiro/hooks/recepcao.ts +256 -0
  36. package/adapters/claude-code/mods/ork-mensageiro/hooks/register.tsx +481 -0
  37. package/adapters/claude-code/mods/ork-mensageiro/orkastery-mod.json +5 -0
  38. package/adapters/claude-code/mods/ork-mensageiro/types/index.d.ts +85 -0
  39. package/adapters/claude-code/mods/ork-mesa/.claude-plugin/plugin.json +14 -0
  40. package/adapters/claude-code/mods/ork-mesa/hooks/cartoes.tsx +246 -0
  41. package/adapters/claude-code/mods/ork-mesa/hooks/decidir.ts +110 -0
  42. package/adapters/claude-code/mods/ork-mesa/hooks/fila.ts +253 -0
  43. package/adapters/claude-code/mods/ork-mesa/hooks/hooks.json +3 -0
  44. package/adapters/claude-code/mods/ork-mesa/hooks/obvia.ts +98 -0
  45. package/adapters/claude-code/mods/ork-mesa/hooks/register.tsx +493 -0
  46. package/adapters/claude-code/mods/ork-mesa/orkastery-mod.json +5 -0
  47. package/adapters/claude-code/mods/ork-mesa/types/index.d.ts +131 -0
  48. package/adapters/claude-code/mods/ork-pista/.claude-plugin/plugin.json +14 -0
  49. package/adapters/claude-code/mods/ork-pista/hooks/acoes.ts +71 -0
  50. package/adapters/claude-code/mods/ork-pista/hooks/ci.ts +121 -0
  51. package/adapters/claude-code/mods/ork-pista/hooks/hooks.json +3 -0
  52. package/adapters/claude-code/mods/ork-pista/hooks/pista.tsx +356 -0
  53. package/adapters/claude-code/mods/ork-pista/hooks/register.tsx +439 -0
  54. package/adapters/claude-code/mods/ork-pista/orkastery-mod.json +5 -0
  55. package/adapters/claude-code/mods/ork-pista/types/index.d.ts +85 -0
  56. package/adapters/claude-code/mods/ork-ponte/.claude-plugin/plugin.json +13 -0
  57. package/adapters/claude-code/mods/ork-ponte/hooks/faixa.tsx +57 -0
  58. package/adapters/claude-code/mods/ork-ponte/hooks/guarda.ts +410 -0
  59. package/adapters/claude-code/mods/ork-ponte/hooks/hooks.json +3 -0
  60. package/adapters/claude-code/mods/ork-ponte/hooks/mostrado.ts +55 -0
  61. package/adapters/claude-code/mods/ork-ponte/hooks/nucleo.ts +157 -0
  62. package/adapters/claude-code/mods/ork-ponte/hooks/papel.ts +42 -0
  63. package/adapters/claude-code/mods/ork-ponte/hooks/register.ts +331 -0
  64. package/adapters/claude-code/mods/ork-ponte/hooks/vigia.ts +76 -0
  65. package/adapters/claude-code/mods/ork-ponte/orkastery-mod.json +5 -0
  66. package/adapters/claude-code/mods/ork-ponte/types/index.d.ts +178 -0
  67. package/adapters/claude-code/mods/ork-portao/.claude-plugin/plugin.json +14 -0
  68. package/adapters/claude-code/mods/ork-portao/hooks/hooks.json +3 -0
  69. package/adapters/claude-code/mods/ork-portao/hooks/lease.ts +201 -0
  70. package/adapters/claude-code/mods/ork-portao/hooks/plano.ts +304 -0
  71. package/adapters/claude-code/mods/ork-portao/hooks/register.ts +339 -0
  72. package/adapters/claude-code/mods/ork-portao/hooks/verify.ts +152 -0
  73. package/adapters/claude-code/mods/ork-portao/orkastery-mod.json +5 -0
  74. package/adapters/claude-code/mods/ork-portao/types/index.d.ts +60 -0
  75. package/adapters/claude-code/mods/ork-radar/.claude-plugin/plugin.json +14 -0
  76. package/adapters/claude-code/mods/ork-radar/hooks/colisao.ts +420 -0
  77. package/adapters/claude-code/mods/ork-radar/hooks/hooks.json +3 -0
  78. package/adapters/claude-code/mods/ork-radar/hooks/mapa.tsx +322 -0
  79. package/adapters/claude-code/mods/ork-radar/hooks/register.tsx +470 -0
  80. package/adapters/claude-code/mods/ork-radar/orkastery-mod.json +5 -0
  81. package/adapters/claude-code/mods/ork-radar/types/index.d.ts +72 -0
  82. package/adapters/claude-code/mods/ork-ritual/.claude-plugin/plugin.json +14 -0
  83. package/adapters/claude-code/mods/ork-ritual/hooks/hooks.json +3 -0
  84. package/adapters/claude-code/mods/ork-ritual/hooks/nota.ts +88 -0
  85. package/adapters/claude-code/mods/ork-ritual/hooks/register.tsx +494 -0
  86. package/adapters/claude-code/mods/ork-ritual/hooks/replay.tsx +271 -0
  87. package/adapters/claude-code/mods/ork-ritual/hooks/ritual.ts +218 -0
  88. package/adapters/claude-code/mods/ork-ritual/orkastery-mod.json +5 -0
  89. package/adapters/claude-code/mods/ork-ritual/types/index.d.ts +106 -0
  90. package/adapters/claude-code/mods/ork-sentinela/.claude-plugin/plugin.json +14 -0
  91. package/adapters/claude-code/mods/ork-sentinela/hooks/alertas.ts +85 -0
  92. package/adapters/claude-code/mods/ork-sentinela/hooks/despacho.ts +156 -0
  93. package/adapters/claude-code/mods/ork-sentinela/hooks/hooks.json +3 -0
  94. package/adapters/claude-code/mods/ork-sentinela/hooks/register.ts +511 -0
  95. package/adapters/claude-code/mods/ork-sentinela/hooks/vigia.ts +186 -0
  96. package/adapters/claude-code/mods/ork-sentinela/orkastery-mod.json +5 -0
  97. package/adapters/claude-code/mods/ork-sentinela/types/index.d.ts +65 -0
  98. package/adapters/claude-code/mods/ork-torre/.claude-plugin/plugin.json +14 -0
  99. package/adapters/claude-code/mods/ork-torre/hooks/agenda.ts +60 -0
  100. package/adapters/claude-code/mods/ork-torre/hooks/detalhe.tsx +236 -0
  101. package/adapters/claude-code/mods/ork-torre/hooks/frases.ts +42 -0
  102. package/adapters/claude-code/mods/ork-torre/hooks/hooks.json +3 -0
  103. package/adapters/claude-code/mods/ork-torre/hooks/painel.tsx +344 -0
  104. package/adapters/claude-code/mods/ork-torre/hooks/rede.ts +97 -0
  105. package/adapters/claude-code/mods/ork-torre/hooks/register.tsx +437 -0
  106. package/adapters/claude-code/mods/ork-torre/orkastery-mod.json +5 -0
  107. package/adapters/claude-code/mods/ork-torre/types/index.d.ts +108 -0
  108. package/adapters/codex/skills/ork/SKILL.md +2 -0
  109. package/adapters/hermes/README.md +64 -1
  110. package/adapters/hermes/bin/ork-conhecimento-colecoes.sh +14 -0
  111. package/adapters/hermes/bin/ork-conhecimento-listar.sh +15 -0
  112. package/adapters/hermes/bin/ork-conhecimento-mostrar.sh +16 -0
  113. package/adapters/hermes/bin/ork-conhecimento-verificar.sh +15 -0
  114. package/adapters/hermes/bin/ork-vigia-batida.py +127 -0
  115. package/adapters/hermes/hermes.plugin.json +4 -0
  116. package/adapters/hermes/hitl-ingress/__init__.py +101 -8
  117. package/adapters/hermes/skills/orkastery-devmaster/SKILL.md +3 -3
  118. package/adapters/openclaw/README.md +83 -10
  119. package/adapters/openclaw/bin/ork-vigia-batida.js +86 -0
  120. package/adapters/openclaw/dist/hitl-ingress.js +106 -9
  121. package/adapters/openclaw/dist/index.js +86 -0
  122. package/adapters/openclaw/openclaw.plugin.json +34 -4
  123. package/adapters/openclaw/package.json +2 -2
  124. package/adapters/openclaw/src/hitl-ingress.ts +103 -9
  125. package/adapters/openclaw/src/index.ts +94 -0
  126. package/assets/adapters-en/claude-code/README.md +403 -0
  127. package/assets/adapters-en/claude-code/agents/ork-check.md +33 -0
  128. package/assets/adapters-en/claude-code/agents/ork-go.md +33 -0
  129. package/assets/adapters-en/claude-code/agents/ork-goal.md +34 -0
  130. package/assets/adapters-en/claude-code/agents/ork-master.md +35 -0
  131. package/assets/adapters-en/claude-code/agents/ork-plan.md +33 -0
  132. package/assets/adapters-en/claude-code/agents/ork-ship.md +33 -0
  133. package/assets/adapters-en/claude-code/commands/check.md +46 -0
  134. package/assets/adapters-en/claude-code/commands/go.md +46 -0
  135. package/assets/adapters-en/claude-code/commands/goal.md +55 -0
  136. package/assets/adapters-en/claude-code/commands/master.md +54 -0
  137. package/assets/adapters-en/claude-code/commands/onboarding.md +22 -0
  138. package/assets/adapters-en/claude-code/commands/ork.md +126 -0
  139. package/assets/adapters-en/claude-code/commands/plan.md +47 -0
  140. package/assets/adapters-en/claude-code/commands/ship.md +45 -0
  141. package/assets/adapters-en/codex/skills/ork/SKILL.md +85 -0
  142. package/assets/adapters-en/hermes/README.md +273 -0
  143. package/assets/adapters-en/hermes/skills/orkastery-devmaster/SKILL.md +169 -0
  144. package/assets/docs-en/markdownlint-cli2.jsonc +22 -0
  145. package/assets/docs-en/padroes/documentacao-de-produto.md +135 -0
  146. package/assets/docs-en/padroes/roadmap-de-produto.md +100 -0
  147. package/assets/docs-en/produto/README.md +14 -0
  148. package/assets/docs-en/produto/_modelo-feature.md +62 -0
  149. package/assets/docs-en/roadmap/README.md +14 -0
  150. package/assets/docs-en/roadmap/_modelo-item.md +75 -0
  151. package/assets/references-en/README.md +25 -0
  152. package/assets/references-en/code-review-axes.md +82 -0
  153. package/assets/references-en/definition-of-done.md +74 -0
  154. package/assets/references-en/performance-checklist.md +53 -0
  155. package/assets/references-en/security-checklist.md +101 -0
  156. package/assets/references-en/testing-patterns.md +51 -0
  157. package/assets/security-scan/defaults/.golangci.yml +2 -0
  158. package/assets/security-scan/defaults/eslint.config.mjs +11 -0
  159. package/assets/security-scan/defaults/gitleaks.toml +3 -0
  160. package/assets/security-scan/defaults/ruff.toml +1 -0
  161. package/assets/security-scan/tools-agil.json +87 -0
  162. package/assets/security-scan/tools.json +625 -0
  163. package/assets/skills-en/core/onboarding/SKILL.md +58 -0
  164. package/assets/skills-en/core/orkastery-bootstrap/SKILL.md +119 -0
  165. package/assets/skills-en/core/thread-state/SKILL.md +88 -0
  166. package/assets/skills-en/governance/decision-triage/SKILL.md +65 -0
  167. package/assets/skills-en/governance/narrative-guardian/SKILL.md +66 -0
  168. package/assets/skills-en/governance/roadmap-keeper/SKILL.md +60 -0
  169. package/assets/skills-en/governance/scope-check-capability-map/SKILL.md +71 -0
  170. package/assets/skills-en/observability/thread-tracing/SKILL.md +63 -0
  171. package/assets/skills-en/phases/check-quality/SKILL.md +103 -0
  172. package/assets/skills-en/phases/go-implementation/SKILL.md +78 -0
  173. package/assets/skills-en/phases/goal-definition/SKILL.md +69 -0
  174. package/assets/skills-en/phases/master-metrics/SKILL.md +72 -0
  175. package/assets/skills-en/phases/plan-specification/SKILL.md +67 -0
  176. package/assets/skills-en/phases/security-remediation/SKILL.md +70 -0
  177. package/assets/skills-en/phases/ship-release/SKILL.md +71 -0
  178. package/assets/skills-en/reviewers/code-reviewer/SKILL.md +61 -0
  179. package/assets/skills-en/reviewers/security-auditor/SKILL.md +93 -0
  180. package/assets/skills-en/reviewers/test-engineer/SKILL.md +66 -0
  181. package/assets/skills-en/reviewers/web-performance-auditor/SKILL.md +63 -0
  182. package/dist/adapters/claude-bg.js +193 -15
  183. package/dist/adapters/codex-controller-worker.js +12 -5
  184. package/dist/adapters/codex-events.js +16 -4
  185. package/dist/adapters/codex.js +13 -2
  186. package/dist/agents-md.js +24 -15
  187. package/dist/atualizar-branch.js +245 -0
  188. package/dist/auditoria.js +13 -11
  189. package/dist/auditrun.js +68 -48
  190. package/dist/autorizacao-duravel.js +327 -0
  191. package/dist/board.js +98 -43
  192. package/dist/branch-de-estado.js +39 -9
  193. package/dist/busca-semantica.js +7 -5
  194. package/dist/catalogo.js +35 -1
  195. package/dist/changelog-reconciliacao.js +290 -0
  196. package/dist/check-agil.js +500 -0
  197. package/dist/check-modo.js +41 -0
  198. package/dist/check-paralelo.js +183 -0
  199. package/dist/ci.js +255 -46
  200. package/dist/ciclos.js +8 -6
  201. package/dist/claim-lint.js +12 -8
  202. package/dist/claims-conferir.js +350 -0
  203. package/dist/claims.js +13 -10
  204. package/dist/company-brain-client.js +45 -3
  205. package/dist/company-brain-journal.js +2 -1
  206. package/dist/company-brain-migration.js +47 -6
  207. package/dist/conducao-batida.js +759 -0
  208. package/dist/conducao-sentinela.js +405 -0
  209. package/dist/conducao-texto.js +12 -18
  210. package/dist/conducao.js +206 -59
  211. package/dist/conhecimento-consulta.js +270 -0
  212. package/dist/conhecimento.js +514 -0
  213. package/dist/conta-esgotada.js +127 -0
  214. package/dist/dado-privado.js +816 -0
  215. package/dist/decisao-autonoma.js +5 -4
  216. package/dist/demo.js +14 -12
  217. package/dist/dependencias-da-worktree.js +25 -0
  218. package/dist/divida.js +38 -36
  219. package/dist/docs.js +103 -82
  220. package/dist/doctor-forja.js +345 -0
  221. package/dist/doctor-pulse-cron.js +294 -27
  222. package/dist/doctor-rede.js +18 -17
  223. package/dist/doctor.js +278 -116
  224. package/dist/dsn-da-memoria.js +417 -0
  225. package/dist/entrega-por-mod.js +503 -0
  226. package/dist/entrega-pr.js +158 -57
  227. package/dist/escopo-escrita.js +10 -1
  228. package/dist/esgotamentos.js +13 -11
  229. package/dist/estado-thread.js +42 -17
  230. package/dist/evalrunner.js +35 -32
  231. package/dist/experiencia-instalacao.js +45 -25
  232. package/dist/fabrica-batida.js +323 -0
  233. package/dist/fabrica-estado.js +49 -16
  234. package/dist/fabrica-publicar.js +3 -1
  235. package/dist/fila-pesada.js +444 -0
  236. package/dist/fix.js +24 -21
  237. package/dist/forja/adaptador.js +6 -0
  238. package/dist/forja/ambiente-de-ci.js +27 -0
  239. package/dist/forja/api.js +256 -0
  240. package/dist/forja/cache.js +89 -0
  241. package/dist/forja/cli.js +60 -0
  242. package/dist/forja/contrato.js +9 -0
  243. package/dist/forja/endereco.js +43 -0
  244. package/dist/forja/estado.js +22 -0
  245. package/dist/forja/executor.js +73 -0
  246. package/dist/forja/github/ci.js +95 -0
  247. package/dist/forja/github/dono-da-rede.js +18 -0
  248. package/dist/forja/github/metadados.js +15 -0
  249. package/dist/forja/github/pedidos.js +200 -0
  250. package/dist/forja/gitlab/ci.js +141 -0
  251. package/dist/forja/gitlab/consultas.js +59 -0
  252. package/dist/forja/gitlab/dono-da-rede.js +43 -0
  253. package/dist/forja/gitlab/metadados.js +15 -0
  254. package/dist/forja/gitlab/pedidos-abertos.js +158 -0
  255. package/dist/forja/gitlab/pedidos.js +141 -0
  256. package/dist/forja/produtor.js +32 -0
  257. package/dist/forja/registro.js +31 -0
  258. package/dist/forja/resolver.js +121 -0
  259. package/dist/forja/texto.js +13 -0
  260. package/dist/forja/versao.js +71 -0
  261. package/dist/forja.js +124 -81
  262. package/dist/gates.js +3 -28
  263. package/dist/handoff.js +33 -19
  264. package/dist/hitl-contract.js +46 -43
  265. package/dist/hitl-curto.js +37 -25
  266. package/dist/hitl-gates.js +13 -11
  267. package/dist/hitl-local-receipt.js +53 -0
  268. package/dist/hitl-local.js +11 -2
  269. package/dist/hitl-lock.js +5 -11
  270. package/dist/hitl-public-receipt.js +11 -0
  271. package/dist/hitl-resumo.js +72 -3
  272. package/dist/hitl-sessions.js +62 -9
  273. package/dist/hitl-tempo-parado.js +4 -4
  274. package/dist/hitl-texto-livre.js +7 -4
  275. package/dist/hitl.js +63 -64
  276. package/dist/horario.js +30 -7
  277. package/dist/hosts.js +155 -134
  278. package/dist/impedimento.js +108 -14
  279. package/dist/index.js +1421 -1019
  280. package/dist/indice-vetorial.js +14 -12
  281. package/dist/indice.js +7 -1
  282. package/dist/init.js +54 -49
  283. package/dist/instabilidade.js +144 -0
  284. package/dist/integracoes-locais.js +4 -0
  285. package/dist/intelligence-graph-cli.js +213 -88
  286. package/dist/intelligence-graph-contexto.js +56 -17
  287. package/dist/intelligence-graph-contract.js +2 -1
  288. package/dist/intelligence-graph-extract-md.js +56 -4
  289. package/dist/intelligence-graph-extract-ts.js +71 -3
  290. package/dist/intelligence-graph-query.js +96 -34
  291. package/dist/leases.js +19 -18
  292. package/dist/ledger-stats.js +17 -16
  293. package/dist/ledger.js +46 -2
  294. package/dist/licoes.js +12 -11
  295. package/dist/liveness.js +6 -3
  296. package/dist/locale-accounts.js +220 -0
  297. package/dist/locale-adapter.js +291 -0
  298. package/dist/locale-ajuda.js +833 -0
  299. package/dist/locale-audit.js +199 -0
  300. package/dist/locale-claims.js +213 -0
  301. package/dist/locale-conducao.js +138 -0
  302. package/dist/locale-conhecimento.js +181 -0
  303. package/dist/locale-decisao.js +205 -0
  304. package/dist/locale-docs.js +230 -0
  305. package/dist/locale-estado.js +101 -0
  306. package/dist/locale-forja.js +290 -0
  307. package/dist/locale-gerados.js +165 -0
  308. package/dist/locale-grafo.js +129 -0
  309. package/dist/locale-master.js +182 -0
  310. package/dist/locale-network.js +524 -0
  311. package/dist/locale-onboarding.js +92 -0
  312. package/dist/locale-operacao-restante.js +931 -0
  313. package/dist/locale-phase.js +151 -0
  314. package/dist/locale-projetos.js +100 -0
  315. package/dist/locale-prompts.js +194 -0
  316. package/dist/locale-pulse.js +349 -0
  317. package/dist/locale-reservas.js +87 -0
  318. package/dist/locale-retry.js +524 -0
  319. package/dist/locale-roadmap.js +102 -0
  320. package/dist/locale-security-scan.js +69 -0
  321. package/dist/locale-setup.js +192 -0
  322. package/dist/locale-ship.js +597 -0
  323. package/dist/locale-thread.js +421 -0
  324. package/dist/locale-verify.js +201 -0
  325. package/dist/locale-worktree.js +246 -0
  326. package/dist/locale.js +1258 -0
  327. package/dist/maestro-runtime.js +22 -3
  328. package/dist/manifest.js +415 -13
  329. package/dist/maquina-ociosa.js +211 -0
  330. package/dist/maquina.js +31 -7
  331. package/dist/master-digest.js +6 -1
  332. package/dist/master-nota-local.js +200 -0
  333. package/dist/master-nota.js +14 -8
  334. package/dist/master.js +139 -65
  335. package/dist/mcp-artifacts.js +11 -2
  336. package/dist/mcp-conhecimento.js +52 -0
  337. package/dist/mcp-experiencia.js +26 -5
  338. package/dist/mcp-git.js +23 -6
  339. package/dist/mcp-grafo.js +7 -4
  340. package/dist/mcp-install.js +73 -8
  341. package/dist/mcp-noctum.js +101 -0
  342. package/dist/mcp-server.js +90 -16
  343. package/dist/mcp-ship-transporte.js +977 -0
  344. package/dist/mcp-ship.js +71 -25
  345. package/dist/mcp-transportes.js +17 -0
  346. package/dist/mcp-verify.js +6 -1
  347. package/dist/memoria.js +63 -58
  348. package/dist/modos-migracao.js +8 -6
  349. package/dist/modos.js +20 -24
  350. package/dist/network-roadmap.js +90 -51
  351. package/dist/noctum-aceite.js +789 -0
  352. package/dist/noctum-arco.js +1418 -0
  353. package/dist/noctum-capacidade.js +141 -0
  354. package/dist/noctum-changelog.js +399 -0
  355. package/dist/noctum-cli.js +482 -0
  356. package/dist/noctum-coordenacao.js +1225 -0
  357. package/dist/noctum-entrega.js +1169 -0
  358. package/dist/noctum-envelope.js +1046 -0
  359. package/dist/noctum-execucao.js +376 -0
  360. package/dist/noctum-fechamento.js +405 -0
  361. package/dist/noctum-fila.js +314 -0
  362. package/dist/noctum-guarda.js +256 -0
  363. package/dist/noctum-integracao.js +507 -0
  364. package/dist/noctum-maquinas.js +383 -0
  365. package/dist/noctum-medicao.js +450 -0
  366. package/dist/noctum-modo.js +362 -0
  367. package/dist/noctum-pergunta-canal.js +96 -0
  368. package/dist/noctum-pergunta.js +115 -0
  369. package/dist/noctum-provas.js +838 -0
  370. package/dist/noctum-reconciliacao.js +591 -0
  371. package/dist/noctum-registro.js +215 -0
  372. package/dist/noctum-relatorio.js +827 -0
  373. package/dist/noctum-resposta.js +207 -0
  374. package/dist/noctum-sessoes-em-voo.js +93 -0
  375. package/dist/noctum-supervisao.js +649 -0
  376. package/dist/noctum-terminal.js +459 -0
  377. package/dist/noctum-transporte.js +655 -0
  378. package/dist/noctum-vigia.js +639 -0
  379. package/dist/ocupacao.js +32 -3
  380. package/dist/onboarding.js +56 -26
  381. package/dist/orkmind.js +48 -25
  382. package/dist/orquestracao.js +44 -47
  383. package/dist/parado-no-condutor.js +174 -127
  384. package/dist/phase.js +144 -63
  385. package/dist/policies.js +37 -4
  386. package/dist/portfolio.js +4 -2
  387. package/dist/postura-local.js +235 -0
  388. package/dist/preflight.js +11 -10
  389. package/dist/procedencia.js +220 -0
  390. package/dist/projeto-alvo.js +186 -24
  391. package/dist/prompts.js +140 -42
  392. package/dist/prova-minima.js +5 -6
  393. package/dist/pulse-cadencia.js +5 -4
  394. package/dist/pulse-consentimento.js +53 -1
  395. package/dist/pulse-delivery.js +151 -24
  396. package/dist/pulse-resposta.js +368 -4
  397. package/dist/pulse.js +188 -22
  398. package/dist/ratelimit.js +16 -6
  399. package/dist/recall.js +14 -13
  400. package/dist/redacao-saida.js +63 -0
  401. package/dist/rede-adesao.js +13 -8
  402. package/dist/rede-forja.js +108 -24
  403. package/dist/rede-status.js +66 -47
  404. package/dist/rede.js +86 -46
  405. package/dist/retomada-checkpoint.js +101 -0
  406. package/dist/retry.js +451 -212
  407. package/dist/roadmap-reservas.js +45 -28
  408. package/dist/roadmap-status.js +129 -33
  409. package/dist/runtime-ambiente.js +5 -0
  410. package/dist/runtime-context.js +284 -32
  411. package/dist/runtime-escada.js +423 -0
  412. package/dist/runtime-profiles.js +103 -46
  413. package/dist/runtimes.js +2 -1
  414. package/dist/saida-segura.js +3 -2
  415. package/dist/security-scan/agil.js +355 -0
  416. package/dist/security-scan/cli.js +76 -0
  417. package/dist/security-scan/compare.js +149 -0
  418. package/dist/security-scan/config.js +93 -0
  419. package/dist/security-scan/detect.js +97 -0
  420. package/dist/security-scan/executor.js +188 -0
  421. package/dist/security-scan/guard.js +180 -0
  422. package/dist/security-scan/imagem-de-lint.js +96 -0
  423. package/dist/security-scan/normalize/actionlint.js +12 -0
  424. package/dist/security-scan/normalize/eslint.js +12 -0
  425. package/dist/security-scan/normalize/gitleaks.js +13 -0
  426. package/dist/security-scan/normalize/golangci-lint.js +13 -0
  427. package/dist/security-scan/normalize/govulncheck.js +36 -0
  428. package/dist/security-scan/normalize/index.js +25 -0
  429. package/dist/security-scan/normalize/npm-audit.js +30 -0
  430. package/dist/security-scan/normalize/osv-scanner.js +32 -0
  431. package/dist/security-scan/normalize/oxlint.js +51 -0
  432. package/dist/security-scan/normalize/pip-audit.js +16 -0
  433. package/dist/security-scan/normalize/ruff.js +12 -0
  434. package/dist/security-scan/normalize/semgrep.js +20 -0
  435. package/dist/security-scan/normalize/trivy.js +33 -0
  436. package/dist/security-scan/normalize/util.js +204 -0
  437. package/dist/security-scan/normalize/zizmor.js +19 -0
  438. package/dist/security-scan/report.js +156 -0
  439. package/dist/security-scan/run.js +448 -0
  440. package/dist/security-scan/tools.js +62 -0
  441. package/dist/security-scan/trabalhador.js +74 -0
  442. package/dist/security-scan/types.js +15 -0
  443. package/dist/session-events.js +26 -5
  444. package/dist/session-watcher-claude.js +81 -20
  445. package/dist/session-watcher.js +340 -42
  446. package/dist/sessoes-contas.js +10 -1
  447. package/dist/sessoes-inventario.js +1 -1
  448. package/dist/sessoes.js +25 -23
  449. package/dist/setup.js +59 -58
  450. package/dist/ship-publicar.js +971 -0
  451. package/dist/ship.js +243 -97
  452. package/dist/steal.js +77 -0
  453. package/dist/superficie.js +14 -11
  454. package/dist/thread-close.js +12 -11
  455. package/dist/thread.js +274 -100
  456. package/dist/tokens.js +15 -11
  457. package/dist/trabalho-thread.js +297 -0
  458. package/dist/transporte-do-canal.js +189 -0
  459. package/dist/types.js +12 -1
  460. package/dist/util.js +13 -3
  461. package/dist/varredura-acoes.js +507 -0
  462. package/dist/verify.js +608 -76
  463. package/dist/vida-da-thread.js +254 -0
  464. package/dist/worktree-local.js +167 -0
  465. package/dist/worktree.js +99 -83
  466. package/dist/yaml.js +27 -8
  467. package/eval/casos/check-quality.json +77 -0
  468. package/eval/casos/security-auditor.json +148 -0
  469. package/eval/casos/security-remediation.json +161 -0
  470. package/eval/fixtures/security-scan/README.md +8 -0
  471. package/eval/fixtures/security-scan/go/.github/workflows/ci.yml +12 -0
  472. package/eval/fixtures/security-scan/go/Dockerfile +4 -0
  473. package/eval/fixtures/security-scan/go/README.md +4 -0
  474. package/eval/fixtures/security-scan/go/config.go +4 -0
  475. package/eval/fixtures/security-scan/go/go.mod +5 -0
  476. package/eval/fixtures/security-scan/go/go.sum +2 -0
  477. package/eval/fixtures/security-scan/go/main.go +31 -0
  478. package/eval/fixtures/security-scan/js/.github/workflows/ci.yml +12 -0
  479. package/eval/fixtures/security-scan/js/Dockerfile +3 -0
  480. package/eval/fixtures/security-scan/js/README.md +4 -0
  481. package/eval/fixtures/security-scan/js/package-lock.json +844 -0
  482. package/eval/fixtures/security-scan/js/package.json +10 -0
  483. package/eval/fixtures/security-scan/js/src/config.js +4 -0
  484. package/eval/fixtures/security-scan/js/src/server.js +17 -0
  485. package/eval/fixtures/security-scan/js/src/util.ts +4 -0
  486. package/eval/fixtures/security-scan/js/tsconfig.json +4 -0
  487. package/eval/fixtures/security-scan/python/.github/workflows/ci.yml +12 -0
  488. package/eval/fixtures/security-scan/python/Dockerfile +3 -0
  489. package/eval/fixtures/security-scan/python/README.md +4 -0
  490. package/eval/fixtures/security-scan/python/app/__init__.py +0 -0
  491. package/eval/fixtures/security-scan/python/app/config.py +2 -0
  492. package/eval/fixtures/security-scan/python/app/main.py +19 -0
  493. package/eval/fixtures/security-scan/python/requirements.txt +1 -0
  494. package/package.json +9 -4
  495. package/references/security-checklist.md +3 -3
  496. package/schemas/code-artifact-graph.v1.schema.json +2 -1
  497. package/skills/README.md +1 -0
  498. package/skills/core/orkastery-bootstrap/SKILL.md +2 -2
  499. package/skills/phases/check-quality/SKILL.md +16 -0
  500. package/skills/phases/go-implementation/SKILL.md +6 -0
  501. package/skills/phases/security-remediation/SKILL.md +70 -0
  502. package/skills/reviewers/security-auditor/SKILL.md +25 -0
@@ -0,0 +1,470 @@
1
+ // O Radar de Colisão do Orkastery (RM-059, fatia F3; ficha M10 do plano dos mods). Na sessão do dono, calcula sem
2
+ // worktree o merge de cada thread aberta contra a origin/main local e entre as threads, cruza os conflitos com os
3
+ // leases e desenha o mapa de calor num painel; a colisão nova vira uma linha na faixa pela ponte, e o que o dono
4
+ // viu entra no registro da ponte (M5 do dono). Lê o núcleo pela ponte (`$.ork`), nunca `.orkastery/` por conta
5
+ // própria; do git, só ls-remote e merge-tree com objetos temporários (D6 do dono). Só lê: nenhum caminho daqui
6
+ // age pelo dono. A lógica fica nos módulos puros ao lado; aqui só os ganchos e as chamadas ao `$`, sempre
7
+ // escritas como `x.noun.metodo(...)`, como o validador exige.
8
+ import { atom, read, update } from 'claude-code'
9
+ import type { EngineInterface, Register } from 'claude-code'
10
+ import type { RadarMapa, RadarThread } from '../types'
11
+ import {
12
+ AMBIENTE_DO_GIT, ambienteDoMerge, anterioresDoMapa, argvDeApagar, argvDoLsRemoteLocal, argvDoLsRemoteRemoto, argvDoMergeTree,
13
+ argvDoTemporario, baseDoMapa, branchCanonica, celulaDoPar, chaveDoPar, colisoesDoMapa, contar, gitdirDoArquivo, guardarNoCache, juntar,
14
+ LEITURAS_EM_PARALELO, lerLsRemote, lerMergeTree, objetosDoComum, ORCAMENTO_DO_MERGE_MS, paginaDosLeases, paresComColisaoNova,
15
+ paresDaRodada, PERIODO_DA_RODADA_MS, PRAZO_DO_MERGE_MS, PRAZO_DO_REMOTO_MS, PRAZO_LOCAL_MS, REF_DA_BASE, REF_REMOTA, refDoStatus,
16
+ resumoDoMapa, rotuloDe, temporarioValido, TETO_DE_PAGINAS_DE_LEASES, threadsDaLista,
17
+ } from './colisao'
18
+ import type { LeaseDePath, Par, Resultado, ThreadDaLista } from './colisao'
19
+ import { desenharPainel, linhasDoPainel, mostradosDoMapa, mostradosVisiveis, novasDepoisDaRodada, textoDaFaixa } from './mapa'
20
+ import type { ElementosDoPainel, RadarMostrado } from './mapa'
21
+
22
+ const MOD = 'ork-radar'
23
+ const PAINEL = 'radar'
24
+ const TITULO = 'Radar'
25
+ const MAPA_VAZIO: RadarMapa = {
26
+ threads: [], base: { ref: REF_DA_BASE, sha: null, remoto: null, situacao: 'nao-conferida', erroRemoto: null },
27
+ celulas: [], omitidas: 0, lacunas: [], lidoEm: null, erro: null,
28
+ }
29
+ const mapa = atom({ plugin: 'ork-radar', key: 'mapa' } as const, MAPA_VAZIO)
30
+ const novas = atom({ plugin: 'ork-radar', key: 'novas' } as const, [] as string[])
31
+ /** O servidor do núcleo pode conectar depois do início da sessão: o papel é conferido de novo nestas esperas. */
32
+ const ESPERAS_DO_PAPEL_MS = [3000, 10000, 30000]
33
+ /** Depois delas, enquanto dormente, a cada 5 min. */
34
+ const ESPERA_LONGA_DO_PAPEL_MS = 300000
35
+ /** Teto de cada leitura pela ponte, que já tem o próprio teto de 10 s no CLI. */
36
+ const PRAZO_DA_PONTE_MS = 20000
37
+ /** A primeira rodada sai logo depois da ativação, fora do início da sessão. */
38
+ const ESPERA_DA_PRIMEIRA_MS = 1000
39
+ /** O que o desenho anotou vai ao registro da ponte, fora do desenho, a cada minuto. */
40
+ const PERIODO_DO_REGISTRO_MS = 60000
41
+
42
+ // O que o módulo guarda entre ganchos e não desenha. Uma recarga do módulo zera tudo, e o `session.start`
43
+ // dispara de novo nela; o mapa, as colisões novas e a linha da faixa (na ponte) ficam em `$.state`.
44
+ let ativa = false
45
+ let desistiu = false
46
+ let ciclo: { cancel: () => void } | undefined
47
+ let registro: { cancel: () => void } | undefined
48
+ let rodando: Promise<void> | undefined
49
+ let pendente = false
50
+ /** O resultado de cada par de SHAs já calculado nesta carga (D12): par que não andou não roda de novo. */
51
+ const cache = new Map<string, Resultado>()
52
+ /** A branch de cada thread pelo `base.branch` do `thread.status` (null: vale a `ork/<slug>`), lida uma vez por carga. */
53
+ const branchDoStatus = new Map<string, string | null>()
54
+ /** O diretório de objetos do repositório, achado uma vez por carga. */
55
+ let objetos: string | undefined
56
+ /**
57
+ * O `lidoEm` do último mapa que uma rodada desta carga gravou. A leitura de `$.state` num despacho vê o momento em
58
+ * que ele começou, e não a gravação que outro despacho faz no meio: quem compara a versão do mapa visto com a atual
59
+ * usa esta variável. Undefined até a primeira rodada desta carga gravar.
60
+ */
61
+ let versaoGravada: number | null | undefined
62
+ /** As colisões (par e arquivo) da rodada anterior; null até a primeira rodada desta carga ler o mapa guardado (D6). */
63
+ let anteriores: Set<string> | null = null
64
+ /**
65
+ * A última linha publicada na faixa. `null` é "não sei": uma carga nova não sabe o que a anterior deixou na faixa
66
+ * da ponte, então a primeira publicação sempre acontece, inclusive a que tira a linha.
67
+ */
68
+ let ultimaFaixa: string | undefined | null = null
69
+ /**
70
+ * O que o desenho pôs na janela de rolagem e ainda não foi ao registro da ponte, com a versão do mapa desenhado
71
+ * (`lidoEm`): anotação de um desenho anterior à última rodada não vale, porque o texto da tela era outro.
72
+ */
73
+ const vistosNoDesenho = new Map<string, { visto: RadarMostrado; versao: number | null }>()
74
+ /** O que já foi ao registro nesta carga, por chave e texto, com a chave do par; sai quando o conflito some do mapa. */
75
+ const jaMostrado = new Map<string, string>()
76
+
77
+ function mensagem(erro: unknown): string {
78
+ return (erro instanceof Error ? erro.message : String(erro)).replace(/\s+/g, ' ').trim().slice(0, 200)
79
+ }
80
+
81
+ function primeiraLinha(texto: string): string {
82
+ return String(texto ?? '').trim().split('\n')[0]?.trim().slice(0, 200) ?? ''
83
+ }
84
+
85
+ /** A leitura com teto próprio: o relógio da sessão recusa a espera que passar dele. */
86
+ function comPrazo<T>($: EngineInterface, leitura: Promise<T>, ms = PRAZO_DA_PONTE_MS): Promise<T> {
87
+ return new Promise<T>((resolver, recusar) => {
88
+ const prazo = $.clock.after(ms, () => recusar(new Error(`a leitura passou de ${Math.round(ms / 1000)} s`)))
89
+ leitura.then(v => { prazo.cancel(); resolver(v) }, e => { prazo.cancel(); recusar(e) })
90
+ })
91
+ }
92
+
93
+ /**
94
+ * O papel pela ponte. `null` só quando o `$.ork` não existe (a ponte não carregou: a chamada nem acontece);
95
+ * uma falha da própria ponte conta como dormente e o papel é conferido de novo depois.
96
+ */
97
+ async function papelDaSessao($: EngineInterface): Promise<string | null> {
98
+ try {
99
+ return String(await $.ork.papel())
100
+ } catch (erro) {
101
+ return erro instanceof TypeError ? null : 'dormente'
102
+ }
103
+ }
104
+
105
+ /** Liga o Radar na sessão do dono: o `/radar`, a rodada de 10 min, o registro do mostrado e a primeira rodada. */
106
+ async function ativar($: EngineInterface): Promise<void> {
107
+ if (ativa) return
108
+ ativa = true
109
+ await $.command.register({
110
+ name: 'radar', description: 'Radar de Colisão: onde cada thread aberta colide com a origin/main e com as outras, antes do SHIP',
111
+ immediate: true,
112
+ })
113
+ ciclo?.cancel()
114
+ ciclo = $.clock.every(PERIODO_DA_RODADA_MS, () => { void pedirRodada($) })
115
+ registro?.cancel()
116
+ registro = $.clock.every(PERIODO_DO_REGISTRO_MS, () => { void registrarMostrados($) })
117
+ $.clock.after(ESPERA_DA_PRIMEIRA_MS, () => { void pedirRodada($) })
118
+ }
119
+
120
+ /** Confere o papel: liga no dono, desiste na fase ou sem a ponte, e tenta de novo enquanto dormente. */
121
+ async function conferirPapel($: EngineInterface, tentativa: number): Promise<void> {
122
+ if (ativa || desistiu) return
123
+ const papel = await papelDaSessao($)
124
+ if (papel === 'dono') return ativar($)
125
+ if (papel === 'fase' || papel === null) {
126
+ desistiu = true
127
+ if (papel === null) $.ui.toast('radar: a ponte ork-ponte não carregou; o Radar de Colisão fica desligado nesta sessão')
128
+ return
129
+ }
130
+ const espera = ESPERAS_DO_PAPEL_MS[tentativa] ?? ESPERA_LONGA_DO_PAPEL_MS
131
+ $.clock.after(espera, () => { void conferirPapel($, tentativa + 1) })
132
+ }
133
+
134
+ /**
135
+ * Uma rodada por vez. Com uma rodada em andamento, o pedido só marca que outra deve rodar logo depois e volta sem
136
+ * nada para esperar: quem pediu (o `/radar`, o botão, o relógio) nunca espera a rodada de outro despacho, cuja
137
+ * espera contaria no orçamento do gancho do host. Devolve a rodada só a quem a começou.
138
+ */
139
+ function pedirRodada($: EngineInterface): Promise<void> | undefined {
140
+ if (!ativa) return undefined
141
+ if (rodando) {
142
+ pendente = true
143
+ return undefined
144
+ }
145
+ const esta = (async () => {
146
+ try {
147
+ do {
148
+ pendente = false
149
+ await rodadaUmaVez($)
150
+ } while (pendente)
151
+ } finally {
152
+ rodando = undefined
153
+ }
154
+ })()
155
+ rodando = esta
156
+ return esta
157
+ }
158
+
159
+ /** A rodada parou antes do mapa: o mapa anterior fica, com o motivo. */
160
+ async function gravarErro($: EngineInterface, erro: string): Promise<void> {
161
+ await update($, mapa, anterior => ({ ...anterior, erro }))
162
+ }
163
+
164
+ /** O SHA da base e de cada ref pedida, pelo `ls-remote` no próprio repositório. */
165
+ async function lerShas($: EngineInterface, raiz: string, refs: readonly string[]): Promise<Record<string, string>> {
166
+ const r = await $.process.run(argvDoLsRemoteLocal(refs), { cwd: raiz, env: { ...AMBIENTE_DO_GIT }, timeoutMs: PRAZO_LOCAL_MS })
167
+ if (r.exitCode !== 0) throw new Error(primeiraLinha(r.stderr) || `o ls-remote saiu ${r.exitCode}`)
168
+ return lerLsRemote(r.stdout)
169
+ }
170
+
171
+ /** O SHA da `main` do `origin` (D4); falha vira "não conferida" e a rodada segue. */
172
+ async function lerRemoto($: EngineInterface, raiz: string): Promise<{ sha: string | null; erro: string | null }> {
173
+ try {
174
+ const r = await $.process.run(argvDoLsRemoteRemoto(), { cwd: raiz, env: { ...AMBIENTE_DO_GIT }, timeoutMs: PRAZO_DO_REMOTO_MS })
175
+ if (r.exitCode !== 0) return { sha: null, erro: primeiraLinha(r.stderr) || `o ls-remote saiu ${r.exitCode}` }
176
+ const sha = lerLsRemote(r.stdout)[REF_REMOTA] ?? null
177
+ return { sha, erro: sha === null ? 'o remoto não listou a main' : null }
178
+ } catch (erro) {
179
+ return { sha: null, erro: mensagem(erro) }
180
+ }
181
+ }
182
+
183
+ /**
184
+ * A branch canônica de cada thread, como o núcleo a decide (`branchDaWorktree`): o `base.branch` do `thread.status`
185
+ * quando começa com `ork/`, senão `ork/<slug>`. O status de cada thread é lido uma vez por carga, algumas por vez;
186
+ * leitura que falha usa a `ork/<slug>` nesta rodada, vira lacuna e é tentada de novo na seguinte.
187
+ */
188
+ async function branchesDasThreads($: EngineInterface, threads: readonly ThreadDaLista[], lacunas: string[]): Promise<Map<string, string>> {
189
+ const faltam = threads.filter(t => !branchDoStatus.has(t.id))
190
+ for (let i = 0; i < faltam.length; i += LEITURAS_EM_PARALELO) {
191
+ await Promise.all(faltam.slice(i, i + LEITURAS_EM_PARALELO).map(async t => {
192
+ try {
193
+ branchDoStatus.set(t.id, refDoStatus(await comPrazo($, $.ork.ler({ consulta: 'thread.status', thread: t.id }))))
194
+ } catch (erro) {
195
+ lacunas.push(`status de ${t.id} não lido; vale a ork/${t.slug} nesta rodada (${mensagem(erro)})`)
196
+ }
197
+ }))
198
+ }
199
+ return new Map(threads.map(t => [t.id, branchCanonica(branchDoStatus.get(t.id) ?? null, t.slug)]))
200
+ }
201
+
202
+ /** Os leases `path:` ativos de todas as páginas da seção `leases` do retrato, até o teto de páginas (D1 e D10). */
203
+ async function lerLeases($: EngineInterface, lacunas: string[]): Promise<LeaseDePath[]> {
204
+ const leases: LeaseDePath[] = []
205
+ let offset = 0
206
+ try {
207
+ for (let pagina = 0; pagina < TETO_DE_PAGINAS_DE_LEASES; pagina++) {
208
+ const p = paginaDosLeases((await comPrazo($, $.ork.retrato({ secao: 'leases', offset }))).retrato)
209
+ leases.push(...p.leases)
210
+ if (p.lacuna) lacunas.push(p.lacuna)
211
+ if (p.proximo === null || p.proximo <= offset) return leases
212
+ offset = p.proximo
213
+ }
214
+ lacunas.push(`leases além de ${TETO_DE_PAGINAS_DE_LEASES} páginas não lidos`)
215
+ } catch (erro) {
216
+ lacunas.push(`leases não lidos (${mensagem(erro)})`)
217
+ }
218
+ return leases
219
+ }
220
+
221
+ /** Os objetos do repositório pelo `.git` da raiz: pasta, ou arquivo de worktree vinculada com `commondir` (D2). */
222
+ async function acharObjetos($: EngineInterface, raiz: string): Promise<string> {
223
+ if (objetos !== undefined) return objetos
224
+ const ponto = juntar(raiz, '.git')
225
+ const st = await $.fs.stat(ponto)
226
+ if (st.kind === 'dir') objetos = juntar(ponto, 'objects')
227
+ else if (st.kind === 'file') {
228
+ const gitdir = gitdirDoArquivo(String(await $.fs.read(ponto)), raiz)
229
+ if (gitdir === null) throw new Error('o .git da raiz não aponta um gitdir')
230
+ const commondir = await $.fs.read(juntar(gitdir, 'commondir')).then(String, () => null)
231
+ objetos = objetosDoComum(gitdir, commondir)
232
+ } else throw new Error('a raiz da sessão não tem .git')
233
+ return objetos
234
+ }
235
+
236
+ /** O temporário da rodada pelo `mktemp -d`; recusa o que não tiver a forma do temporário do Radar (D5). */
237
+ async function criarTemporario($: EngineInterface): Promise<string> {
238
+ const r = await $.process.run(argvDoTemporario(), { timeoutMs: PRAZO_LOCAL_MS })
239
+ const temporario = r.exitCode === 0 ? temporarioValido(r.stdout) : null
240
+ if (temporario === null) throw new Error(`o mktemp devolveu ${JSON.stringify(primeiraLinha(r.stdout || r.stderr))}`)
241
+ return temporario
242
+ }
243
+
244
+ /**
245
+ * Os pares que o cache não tinha: um `merge-tree` por par de SHAs, com objetos novos num temporário da rodada e
246
+ * os do repositório como alternativo (D2, D3 e D5), dentro do orçamento da rodada (D12). O temporário sai sempre.
247
+ */
248
+ async function calcularPares($: EngineInterface, raiz: string, pares: readonly Par[], resultados: Map<string, Resultado>,
249
+ motivos: Map<string, string>, lacunas: string[]): Promise<void> {
250
+ const semCalculo = (motivo: string) => { for (const p of pares) motivos.set(chaveDoPar(p.shaA, p.shaB), motivo) }
251
+ let dirDosObjetos: string
252
+ try {
253
+ dirDosObjetos = await acharObjetos($, raiz)
254
+ } catch (erro) {
255
+ lacunas.push(`objetos do repositório não achados (${mensagem(erro)})`)
256
+ return semCalculo('não calculado: objetos do repositório não achados')
257
+ }
258
+ let temporario: string
259
+ try {
260
+ temporario = await criarTemporario($)
261
+ } catch (erro) {
262
+ lacunas.push(`temporário recusado (${mensagem(erro)})`)
263
+ return semCalculo('não calculado: temporário recusado')
264
+ }
265
+ const inicio = await $.clock.now()
266
+ try {
267
+ for (const p of pares) {
268
+ const chave = chaveDoPar(p.shaA, p.shaB)
269
+ if (resultados.has(chave)) continue
270
+ if ((await $.clock.now()) - inicio >= ORCAMENTO_DO_MERGE_MS) {
271
+ motivos.set(chave, `não calculado nesta rodada (orçamento de ${ORCAMENTO_DO_MERGE_MS / 1000} s)`)
272
+ continue
273
+ }
274
+ let r: Resultado
275
+ try {
276
+ r = lerMergeTree(await $.process.run(argvDoMergeTree(p.shaA, p.shaB),
277
+ { cwd: raiz, env: ambienteDoMerge(temporario, dirDosObjetos), timeoutMs: PRAZO_DO_MERGE_MS }))
278
+ } catch (erro) {
279
+ r = { estado: 'erro', erro: mensagem(erro) }
280
+ }
281
+ resultados.set(chave, r)
282
+ guardarNoCache(cache, chave, r)
283
+ }
284
+ } finally {
285
+ const apagado = await $.process.run(argvDeApagar(temporario), { timeoutMs: PRAZO_LOCAL_MS }).then(r => r.exitCode === 0, () => false)
286
+ if (!apagado) lacunas.push(`temporário ${temporario} não apagado`)
287
+ }
288
+ }
289
+
290
+ /** Uma rodada inteira: threads e leases pela ponte, SHAs pelo `ls-remote`, pares pelo `merge-tree`, mapa no estado. */
291
+ async function rodadaUmaVez($: EngineInterface): Promise<void> {
292
+ let raiz: string
293
+ try {
294
+ const contexto = await comPrazo($, $.ork.contexto())
295
+ if (contexto.papel !== 'dono') return
296
+ raiz = contexto.raiz
297
+ } catch (erro) {
298
+ return gravarErro($, `o contexto da ponte falhou (${mensagem(erro)})`)
299
+ }
300
+ const lacunas: string[] = []
301
+ let escolha: ReturnType<typeof threadsDaLista>
302
+ try {
303
+ escolha = threadsDaLista(await comPrazo($, $.ork.ler({ consulta: 'thread.list' })))
304
+ } catch (erro) {
305
+ return gravarErro($, `a lista das threads abertas falhou (${mensagem(erro)})`)
306
+ }
307
+ const leases = await lerLeases($, lacunas)
308
+ const refs = await branchesDasThreads($, escolha.threads, lacunas)
309
+ let shas: Record<string, string>
310
+ try {
311
+ shas = await lerShas($, raiz, [...new Set(refs.values())])
312
+ } catch (erro) {
313
+ return gravarErro($, `o ls-remote do repositório falhou (${mensagem(erro)})`)
314
+ }
315
+ const threads: RadarThread[] = escolha.threads.map((t, i) => {
316
+ const branch = refs.get(t.id) as string
317
+ const sha = shas[branch] ?? null
318
+ return { id: t.id, fase: t.fase, rotulo: rotuloDe(i), branch, sha, motivo: sha === null ? 'sem branch local' : null }
319
+ })
320
+ const shaDaBase = shas[REF_DA_BASE] ?? null
321
+ if (shaDaBase === null) lacunas.push(`sem ${REF_DA_BASE} local: os pares com a base ficaram de fora`)
322
+ const remoto = await lerRemoto($, raiz)
323
+ const pares = paresDaRodada(threads, shaDaBase)
324
+ const resultados = new Map<string, Resultado>()
325
+ const motivos = new Map<string, string>()
326
+ const calcular: Par[] = []
327
+ for (const p of pares) {
328
+ const chave = chaveDoPar(p.shaA, p.shaB)
329
+ const guardado = cache.get(chave)
330
+ if (p.shaA === p.shaB) resultados.set(chave, { estado: 'limpa' })
331
+ else if (guardado !== undefined) resultados.set(chave, guardado)
332
+ else calcular.push(p)
333
+ }
334
+ if (calcular.length > 0) await calcularPares($, raiz, calcular, resultados, motivos, lacunas)
335
+ const celulas = pares.map(p => {
336
+ const chave = chaveDoPar(p.shaA, p.shaB)
337
+ return celulaDoPar(p, resultados.get(chave) ?? null, leases, motivos.get(chave))
338
+ })
339
+ const novo: RadarMapa = {
340
+ threads, base: baseDoMapa(shaDaBase, remoto), celulas, omitidas: escolha.omitidas, lacunas, lidoEm: await $.clock.now(), erro: null,
341
+ }
342
+ // D6: nova é a colisão que não estava na rodada anterior desta sessão. Depois de uma recarga do módulo, a rodada
343
+ // anterior é a do mapa guardado em `$.state`, lido antes de o mapa novo ir por cima dele.
344
+ if (anteriores === null) anteriores = anterioresDoMapa(await read($, mapa))
345
+ await update($, mapa, () => novo)
346
+ versaoGravada = novo.lidoEm
347
+ const novosPares = paresComColisaoNova(anteriores, novo)
348
+ anteriores = new Set(colisoesDoMapa(novo))
349
+ await update($, novas, antigas => novasDepoisDaRodada(antigas, novosPares, novo))
350
+ await publicarFaixa($)
351
+ await registrarMostrados($)
352
+ }
353
+
354
+ /** O painel do Radar está posto e à vista do dono. */
355
+ async function painelAVista($: EngineInterface): Promise<boolean> {
356
+ const paineis = await $.ui.panes().catch(() => [])
357
+ return paineis.some(p => p.id === PAINEL && p.isPlaced && p.isShown)
358
+ }
359
+
360
+ /** Uma falha que o dono precisa ver: entra nas lacunas do mapa, que o painel e o `/radar` mostram, uma vez só. */
361
+ async function anotarLacuna($: EngineInterface, lacuna: string): Promise<void> {
362
+ await update($, mapa, m => (m.lacunas.includes(lacuna) ? m : { ...m, lacunas: [...m.lacunas, lacuna] }))
363
+ }
364
+
365
+ /** A linha da faixa pela ponte (D6 e D7), só quando o texto muda; sem colisão nova não vista, a linha sai. */
366
+ async function publicarFaixa($: EngineInterface): Promise<void> {
367
+ const texto = textoDaFaixa(await read($, mapa), await read($, novas))
368
+ if (texto === ultimaFaixa) return
369
+ try {
370
+ if (texto === undefined) await $.ork.faixa({ mod: MOD })
371
+ else await $.ork.faixa({ mod: MOD, texto })
372
+ ultimaFaixa = texto
373
+ } catch (erro) {
374
+ // A linha fica como estava; o motivo vai às lacunas, e a próxima rodada tenta de novo.
375
+ await anotarLacuna($, `linha da faixa não publicada (${mensagem(erro)})`)
376
+ }
377
+ }
378
+
379
+ /**
380
+ * O que o dono viu (conflito por conflito) deixa de ser novo, e a linha da faixa acompanha. Só vale para a versão
381
+ * do mapa que ele viu: se uma rodada gravou outra no meio, a colisão nova dela continua nova.
382
+ */
383
+ async function marcarVistos($: EngineInterface, vistos: readonly RadarMostrado[], versao: number | null): Promise<void> {
384
+ if (versaoGravada === undefined || versao !== versaoGravada) return
385
+ const pares = new Set(vistos.map(v => v.chave.slice('radar:'.length)))
386
+ if (!(await read($, novas)).some(p => pares.has(p))) return
387
+ await update($, novas, atuais => atuais.filter(p => !pares.has(p)))
388
+ await publicarFaixa($)
389
+ }
390
+
391
+ /** Registra pela ponte o que o dono viu, uma vez por chave e texto enquanto o conflito existir no mapa. */
392
+ async function registrar($: EngineInterface, canal: 'painel' | 'transcript', vistos: readonly RadarMostrado[]): Promise<RadarMostrado[]> {
393
+ const falharam: RadarMostrado[] = []
394
+ for (const v of vistos) {
395
+ const chave = `${v.chave}|${v.texto}`
396
+ if (jaMostrado.has(chave)) continue
397
+ try {
398
+ await $.ork.mostrado({ mod: MOD, canal, chave: v.chave, thread: v.thread, texto: v.texto })
399
+ jaMostrado.set(chave, v.chave)
400
+ } catch {
401
+ falharam.push(v)
402
+ }
403
+ }
404
+ return falharam
405
+ }
406
+
407
+ /**
408
+ * O que o desenho pôs na janela de rolagem do painel posto e à vista vai ao registro da ponte (M5 do dono), fora
409
+ * do desenho: o Telegram não repete o que o dono já viu no terminal, e nada que não estava na tela entra.
410
+ */
411
+ async function registrarMostrados($: EngineInterface): Promise<void> {
412
+ const m = await read($, mapa)
413
+ // O registro já feito sai quando o par deixa de estar em conflito (a chave do par, porque o texto pode ser o
414
+ // pedaço que coube na largura); a anotação de outra versão do mapa sai sempre, e o próximo desenho anota de novo.
415
+ const emConflito = new Set(mostradosDoMapa(m).map(v => v.chave))
416
+ for (const [chave, par] of [...jaMostrado]) if (!emConflito.has(par)) jaMostrado.delete(chave)
417
+ const versao = versaoGravada
418
+ for (const [chave, a] of [...vistosNoDesenho]) if (versao === undefined || a.versao !== versao) vistosNoDesenho.delete(chave)
419
+ if (vistosNoDesenho.size === 0 || versao === undefined) return
420
+ // Com o painel escondido, o que foi anotado é descartado: o próximo desenho à vista anota de novo.
421
+ if (!await painelAVista($)) { vistosNoDesenho.clear(); return }
422
+ const vistos = [...vistosNoDesenho.values()].map(a => a.visto)
423
+ vistosNoDesenho.clear()
424
+ for (const v of await registrar($, 'painel', vistos)) vistosNoDesenho.set(`${v.chave}|${v.texto}`, { visto: v, versao })
425
+ await marcarVistos($, vistos, versao)
426
+ }
427
+
428
+ /** O desenho anota o que pôs na janela, com a versão do mapa desenhado; quem registra é o laço, fora do desenho. */
429
+ function anotarVistos(vistos: readonly RadarMostrado[], versao: number | null): void {
430
+ for (const v of vistos) vistosNoDesenho.set(`${v.chave}|${v.texto}`, { visto: v, versao })
431
+ }
432
+
433
+ export const register: Register = on => {
434
+ on('session.start', async ($, e, next) => {
435
+ if (e.isInteractive) await conferirPapel($, 0)
436
+ return next(e)
437
+ })
438
+
439
+ // `/radar` abre o painel, roda uma rodada agora e responde o resumo, que o dono lê ali mesmo: os conflitos do
440
+ // resumo entram no registro da ponte e deixam de ser novos. Com uma rodada de outro despacho em andamento, não a
441
+ // espera: responde o mapa que há e a rodada pedida roda logo depois. O painel não abre sozinho (D6). O comando só
442
+ // existe com o Radar ligado; a conferência do papel aqui só cobre o caso raro de o comando ter sobrado de outra carga.
443
+ on('command.run', { command: 'radar' }, async $ => {
444
+ if (!ativa && !desistiu) await conferirPapel($, ESPERAS_DO_PAPEL_MS.length)
445
+ if (!ativa) return { text: 'O Radar de Colisão só roda na sessão do dono de um projeto do Orkastery.' }
446
+ const aberto = await $.ui.open({ id: PAINEL, title: TITULO }).catch(() => null)
447
+ const propria = pedirRodada($)
448
+ if (propria) await propria
449
+ const m = await read($, mapa)
450
+ const vistos = mostradosDoMapa(m)
451
+ const falharam = await registrar($, 'transcript', vistos)
452
+ if (falharam.length > 0) {
453
+ await anotarLacuna($, `${contar(falharam.length, 'conflito não foi', 'conflitos não foram')} ao registro do mostrado da ponte`)
454
+ }
455
+ await marcarVistos($, vistos, m.lidoEm)
456
+ const andamento = propria ? '' : '\nUma rodada já estava em andamento: o painel mostra a conta nova quando ela terminar.'
457
+ const espera = aberto && !aberto.isPlaced ? `\nO painel espera lugar: ${aberto.reason}` : ''
458
+ return { text: resumoDoMapa(m) + andamento + espera }
459
+ })
460
+
461
+ // O painel: a matriz (Raster no terminal, tabela fora dele), a lista de conflitos e o botão Atualizar. O desenho
462
+ // nunca escreve: só anota o que pôs na janela de rolagem, cortado na largura do painel, para o registro do mostrado.
463
+ on('ui.render', { component: 'Pane', requestId: PAINEL }, async ($, e) => {
464
+ const el = $.ui.resolve(e) as unknown as ElementosDoPainel
465
+ const m = await read($, mapa)
466
+ const linhas = linhasDoPainel(m, await read($, novas), await $.clock.now())
467
+ anotarVistos(mostradosVisiveis(linhas, e.props.scroll, e.props.bodyColumns), m.lidoEm)
468
+ return desenharPainel(el, m, linhas, { terminal: e.surface === 'terminal', atualizar: () => { void pedirRodada($) } })
469
+ })
470
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "formato": "ork.mod/v1",
3
+ "papeis": ["dono"],
4
+ "claudeCodeMinimo": "2.1.287"
5
+ }
@@ -0,0 +1,72 @@
1
+ // Contrato do estado do Radar de Colisão (`ork-radar`, RM-059, fatia F3). O motor exige que cada chave de
2
+ // `$.state` do mod esteja declarada aqui, em `PluginState`. O Radar não acrescenta noun ao `$`: lê o núcleo
3
+ // pelo `$.ork` da `ork-ponte`, que o plugin.json lista em `dependencies`. Sem import nem referência.
4
+
5
+ /** Como um par ficou na última conta: limpo, em conflito, com erro do git ou ainda não calculado. */
6
+ export type RadarEstado = 'limpa' | 'conflito' | 'erro' | 'pendente'
7
+
8
+ /** Um lease `path:<glob>` ativo que cobre um arquivo em conflito, com a thread que o segura. */
9
+ export type RadarLease = { lease: string; thread: string }
10
+
11
+ /** Um arquivo em conflito num par e os leases que o cobrem. */
12
+ export type RadarArquivo = { caminho: string; leases: RadarLease[] }
13
+
14
+ /** Um par: uma thread contra a base (`@main`) ou duas threads, com o que o `merge-tree` disse dele. */
15
+ export type RadarCelula = {
16
+ /** Os dois lados em ordem, separados por `|`. */
17
+ par: string
18
+ a: string
19
+ b: string
20
+ estado: RadarEstado
21
+ arquivos: RadarArquivo[]
22
+ /** A primeira linha do erro do git, ou o motivo de o par não ter sido calculado. */
23
+ erro: string | null
24
+ }
25
+
26
+ /** Uma thread aberta da rodada: o rótulo do mapa, a branch e o SHA dela, ou por que não tem. */
27
+ export type RadarThread = {
28
+ id: string
29
+ fase: string
30
+ /** O nome curto no mapa: de 1 a 9, depois a, b e c. */
31
+ rotulo: string
32
+ /** A ref da branch, como o `ls-remote` a lista. */
33
+ branch: string
34
+ sha: string | null
35
+ motivo: string | null
36
+ }
37
+
38
+ /** A base da conta: a `origin/main` local e o que o remoto disse dela. */
39
+ export type RadarBase = {
40
+ ref: string
41
+ sha: string | null
42
+ remoto: string | null
43
+ /** `igual` ao remoto, `diferente` dele, ou `nao-conferida` quando o remoto não respondeu. */
44
+ situacao: 'igual' | 'diferente' | 'nao-conferida'
45
+ erroRemoto: string | null
46
+ }
47
+
48
+ /** O mapa de uma rodada. */
49
+ export type RadarMapa = {
50
+ threads: RadarThread[]
51
+ base: RadarBase
52
+ celulas: RadarCelula[]
53
+ /** Threads abertas que ficaram de fora pelo teto da rodada. */
54
+ omitidas: number
55
+ /** O que não veio inteiro: os leases, a branch de uma thread, o orçamento do `merge-tree`, o temporário. */
56
+ lacunas: string[]
57
+ /** Quando a rodada terminou, em ms desde a época; null antes da primeira. */
58
+ lidoEm: number | null
59
+ /** Por que a rodada não foi feita, quando não foi. */
60
+ erro: string | null
61
+ }
62
+
63
+ declare module 'claude-code' {
64
+ interface PluginState {
65
+ 'ork-radar': {
66
+ /** O mapa da última rodada. */
67
+ mapa: RadarMapa
68
+ /** Os pares com colisão nova que o dono ainda não viu no painel nem no `/radar` (D6). */
69
+ novas: string[]
70
+ }
71
+ }
72
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "ork-ritual",
3
+ "version": "0.1.0",
4
+ "description": "Ritual do MASTER do Orkastery no Claude Code: as entregas que esperam a sua nota, a prova de cada uma (o que foi entregue, o índice derivado do ledger e as claims com comando e estado, uma por vez) e a nota de 0 a 5 pelo diálogo nativo do host. O mod nunca dá a nota pelo dono.",
5
+ "author": {
6
+ "name": "Orkastery contributors",
7
+ "url": "https://github.com/orkastery"
8
+ },
9
+ "homepage": "https://orkastery.com/",
10
+ "repository": "https://github.com/orkastery/orkastery",
11
+ "license": "MIT",
12
+ "types": "./types/index.d.ts",
13
+ "dependencies": ["ork-ponte"]
14
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "modules": ["./register.tsx"]
3
+ }
@@ -0,0 +1,88 @@
1
+ // O que o Ritual do MASTER pede ao nucleo para a nota (RM-059, fatia F8, T8.3), em funcoes puras. Sao dois caminhos, e
2
+ // em nenhum o mod da a nota pelo dono:
3
+ // - "Dar a nota" chama `ork_request_master_note` com exatamente `{ threadId }`: o nucleo abre o dialogo nativo do host,
4
+ // o dono escolhe de 0 a 5 e escreve o porque, e o nucleo grava o MASTER com o recibo do ingresso local. Nenhum
5
+ // argumento leva nota, porque ou texto.
6
+ // - "Pelo Telegram" (a reserva, so no clique do dono, D7) roda `ork master pedir <thread> --formato json`: o nucleo guarda
7
+ // o pedido de nota e devolve o codigo que o dono responde pelo Telegram. O pedido nao responde nada.
8
+ // So importa tipos.
9
+ import type { RitualNota, RitualReserva } from '../types'
10
+ import { exigirThread, umaLinha } from './ritual'
11
+
12
+ export const FERRAMENTA_DA_NOTA = 'ork_request_master_note'
13
+ /**
14
+ * O dialogo do nucleo fecha em ate 300 s, a gravacao espera a trava da thread por ate 60 s e a resposta sai depois da
15
+ * soltura ao fechar (o git das reservas e o runtime); o botao espera tudo isso, com folga.
16
+ */
17
+ export const PRAZO_DA_NOTA_MS = 430000
18
+ /** O pedido pelo Telegram so grava o pedido de nota e devolve o codigo. */
19
+ export const PRAZO_DA_RESERVA_MS = 60000
20
+
21
+ /** Os argumentos do "Dar a nota": so o id da thread, nada que seja nota. */
22
+ export function argsDaNota(thread: string): { threadId: string } {
23
+ return { threadId: exigirThread(thread) }
24
+ }
25
+
26
+ /** O argv da reserva pelo Telegram, montado so aqui: o pedido de nota do nucleo, nunca a nota. */
27
+ export function argvDaReserva(thread: string): string[] {
28
+ return ['ork', 'master', 'pedir', exigirThread(thread), '--formato', 'json']
29
+ }
30
+
31
+ /** O que o cartao diz do "Dar a nota", a partir do que o nucleo devolveu (`{ ok, estado, repetida, score }`). */
32
+ export function resultadoDaNota(resposta: unknown, thread: string, agora: number): RitualNota {
33
+ const r = (resposta && typeof resposta === 'object' ? resposta : {}) as { ok?: unknown; estado?: unknown; repetida?: unknown; score?: unknown }
34
+ const nota = typeof r.score === 'number' && Number.isInteger(r.score) ? `${r.score}/5` : 'a nota'
35
+ if (r.ok === true && r.estado === 'registrada') {
36
+ return r.repetida === true
37
+ ? { estado: 'repetida', texto: `a ${thread} já tinha nota humana ${nota} no núcleo`, em: agora }
38
+ : { estado: 'registrada', texto: `nota ${nota} da ${thread} registrada pelo diálogo nativo`, em: agora }
39
+ }
40
+ return { estado: 'pendente', texto: 'o diálogo fechou sem nota; a entrega segue esperando', em: agora }
41
+ }
42
+
43
+ /**
44
+ * O que o cartao diz quando o "Dar a nota" falhou: sem dialogo nativo (ou sem a ferramenta) a reserva aparece; a thread
45
+ * que seguiu ocupada depois da resposta volta a ter o botao, sem reserva.
46
+ */
47
+ export function notaDoErro(erro: unknown, agora: number): RitualNota {
48
+ const motivo = umaLinha(erro)
49
+ if (/master\.nota\.trava-ocupada/.test(motivo)) {
50
+ return { estado: 'pendente', texto: 'a thread seguiu ocupada e a nota não foi gravada; dê de novo', em: agora }
51
+ }
52
+ if (/master\.nota\.sem-dialogo/.test(motivo)) {
53
+ return { estado: 'sem-dialogo', texto: 'o diálogo nativo não abriu: este cliente MCP não abre formulário; peça a nota pelo Telegram', em: agora }
54
+ }
55
+ if (/mcp\.tool\.unknown/.test(motivo)) {
56
+ return { estado: 'sem-dialogo', texto: 'o núcleo desta sessão não tem a ferramenta da nota; atualize o Orkastery ou peça a nota pelo Telegram', em: agora }
57
+ }
58
+ return { estado: 'erro', texto: `o Dar a nota falhou: ${motivo}; peça a nota pelo Telegram`, em: agora }
59
+ }
60
+
61
+ /** A reserva aparece quando o dialogo nao serviu (sem dialogo nativo ou erro do nucleo). */
62
+ export function pedeReserva(nota: RitualNota | undefined): boolean {
63
+ return nota?.estado === 'sem-dialogo' || nota?.estado === 'erro'
64
+ }
65
+
66
+ /** Depois de uma recarga do modulo nenhum dialogo esta em curso: o que ficou `abrindo` volta a ter o botao. */
67
+ export function soltarAbrindo(notas: Readonly<Record<string, RitualNota>>): Record<string, RitualNota> {
68
+ return Object.fromEntries(Object.entries(notas).map(([t, n]) => [t, n.estado === 'abrindo'
69
+ ? { estado: 'pendente' as const, texto: 'o diálogo ficou sem resposta (o Ritual recarregou); a entrega segue esperando', em: n.em } : n]))
70
+ }
71
+
72
+ /** A saida do `ork master pedir --formato json`: o codigo que o dono responde pelo Telegram, ou o motivo da falha. */
73
+ export function lerReserva(saida: { exitCode: number; stdout: string; stderr: string }, thread: string, agora: number): RitualReserva {
74
+ if (saida.exitCode !== 0) {
75
+ const fim = umaLinha(saida.stderr.trim().split('\n').slice(-2).join(' ') || `saiu ${saida.exitCode}`)
76
+ return { estado: 'erro', texto: `o pedido pelo Telegram falhou: ${fim}`, codigo: null, em: agora }
77
+ }
78
+ let p: { contrato?: unknown; thread?: unknown; codigo?: unknown }
79
+ try {
80
+ p = JSON.parse(saida.stdout) as typeof p
81
+ } catch {
82
+ return { estado: 'erro', texto: 'o pedido pelo Telegram não devolveu o contrato ork.master-nota/v1', codigo: null, em: agora }
83
+ }
84
+ if (!p || p.contrato !== 'ork.master-nota/v1' || p.thread !== thread || typeof p.codigo !== 'string' || !/^[0-9A-Za-z]{3,8}$/.test(p.codigo)) {
85
+ return { estado: 'erro', texto: 'o pedido pelo Telegram não devolveu o contrato ork.master-nota/v1', codigo: null, em: agora }
86
+ }
87
+ return { estado: 'pronta', texto: `pelo Telegram, responda: ${p.codigo} <0 a 5> <porquê>`, codigo: p.codigo, em: agora }
88
+ }