@orkastery/cli 0.5.6 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (396) 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 +37 -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/ork-mensageiro/.claude-plugin/plugin.json +14 -0
  16. package/adapters/claude-code/mods/ork-mensageiro/hooks/caixa.ts +215 -0
  17. package/adapters/claude-code/mods/ork-mensageiro/hooks/hooks.json +3 -0
  18. package/adapters/claude-code/mods/ork-mensageiro/hooks/painel.tsx +165 -0
  19. package/adapters/claude-code/mods/ork-mensageiro/hooks/recepcao.ts +256 -0
  20. package/adapters/claude-code/mods/ork-mensageiro/hooks/register.tsx +481 -0
  21. package/adapters/claude-code/mods/ork-mensageiro/orkastery-mod.json +5 -0
  22. package/adapters/claude-code/mods/ork-mensageiro/types/index.d.ts +85 -0
  23. package/adapters/claude-code/mods/ork-pista/hooks/ci.ts +9 -5
  24. package/adapters/claude-code/mods/ork-ritual/.claude-plugin/plugin.json +14 -0
  25. package/adapters/claude-code/mods/ork-ritual/hooks/hooks.json +3 -0
  26. package/adapters/claude-code/mods/ork-ritual/hooks/nota.ts +88 -0
  27. package/adapters/claude-code/mods/ork-ritual/hooks/register.tsx +494 -0
  28. package/adapters/claude-code/mods/ork-ritual/hooks/replay.tsx +271 -0
  29. package/adapters/claude-code/mods/ork-ritual/hooks/ritual.ts +218 -0
  30. package/adapters/claude-code/mods/ork-ritual/orkastery-mod.json +5 -0
  31. package/adapters/claude-code/mods/ork-ritual/types/index.d.ts +106 -0
  32. package/adapters/claude-code/mods/ork-sentinela/hooks/vigia.ts +1 -1
  33. package/adapters/codex/skills/ork/SKILL.md +2 -0
  34. package/adapters/hermes/README.md +64 -1
  35. package/adapters/hermes/bin/ork-conhecimento-colecoes.sh +14 -0
  36. package/adapters/hermes/bin/ork-conhecimento-listar.sh +15 -0
  37. package/adapters/hermes/bin/ork-conhecimento-mostrar.sh +16 -0
  38. package/adapters/hermes/bin/ork-conhecimento-verificar.sh +15 -0
  39. package/adapters/hermes/bin/ork-vigia-batida.py +127 -0
  40. package/adapters/hermes/hermes.plugin.json +4 -0
  41. package/adapters/hermes/hitl-ingress/__init__.py +101 -8
  42. package/adapters/hermes/skills/orkastery-devmaster/SKILL.md +2 -2
  43. package/adapters/openclaw/README.md +81 -9
  44. package/adapters/openclaw/bin/ork-vigia-batida.js +86 -0
  45. package/adapters/openclaw/dist/hitl-ingress.js +106 -9
  46. package/adapters/openclaw/dist/index.js +86 -0
  47. package/adapters/openclaw/openclaw.plugin.json +34 -4
  48. package/adapters/openclaw/package.json +2 -2
  49. package/adapters/openclaw/src/hitl-ingress.ts +103 -9
  50. package/adapters/openclaw/src/index.ts +94 -0
  51. package/assets/adapters-en/claude-code/README.md +403 -0
  52. package/assets/adapters-en/claude-code/agents/ork-check.md +33 -0
  53. package/assets/adapters-en/claude-code/agents/ork-go.md +33 -0
  54. package/assets/adapters-en/claude-code/agents/ork-goal.md +34 -0
  55. package/assets/adapters-en/claude-code/agents/ork-master.md +35 -0
  56. package/assets/adapters-en/claude-code/agents/ork-plan.md +33 -0
  57. package/assets/adapters-en/claude-code/agents/ork-ship.md +33 -0
  58. package/assets/adapters-en/claude-code/commands/check.md +46 -0
  59. package/assets/adapters-en/claude-code/commands/go.md +46 -0
  60. package/assets/adapters-en/claude-code/commands/goal.md +55 -0
  61. package/assets/adapters-en/claude-code/commands/master.md +54 -0
  62. package/assets/adapters-en/claude-code/commands/onboarding.md +22 -0
  63. package/assets/adapters-en/claude-code/commands/ork.md +126 -0
  64. package/assets/adapters-en/claude-code/commands/plan.md +47 -0
  65. package/assets/adapters-en/claude-code/commands/ship.md +45 -0
  66. package/assets/adapters-en/codex/skills/ork/SKILL.md +85 -0
  67. package/assets/adapters-en/hermes/README.md +273 -0
  68. package/assets/adapters-en/hermes/skills/orkastery-devmaster/SKILL.md +169 -0
  69. package/assets/references-en/README.md +25 -0
  70. package/assets/references-en/code-review-axes.md +82 -0
  71. package/assets/references-en/definition-of-done.md +74 -0
  72. package/assets/references-en/performance-checklist.md +53 -0
  73. package/assets/references-en/security-checklist.md +101 -0
  74. package/assets/references-en/testing-patterns.md +51 -0
  75. package/assets/security-scan/defaults/.golangci.yml +2 -0
  76. package/assets/security-scan/defaults/eslint.config.mjs +11 -0
  77. package/assets/security-scan/defaults/gitleaks.toml +3 -0
  78. package/assets/security-scan/defaults/ruff.toml +1 -0
  79. package/assets/security-scan/tools-agil.json +87 -0
  80. package/assets/security-scan/tools.json +625 -0
  81. package/assets/skills-en/core/onboarding/SKILL.md +58 -0
  82. package/assets/skills-en/core/orkastery-bootstrap/SKILL.md +119 -0
  83. package/assets/skills-en/core/thread-state/SKILL.md +88 -0
  84. package/assets/skills-en/governance/decision-triage/SKILL.md +65 -0
  85. package/assets/skills-en/governance/narrative-guardian/SKILL.md +66 -0
  86. package/assets/skills-en/governance/roadmap-keeper/SKILL.md +60 -0
  87. package/assets/skills-en/governance/scope-check-capability-map/SKILL.md +71 -0
  88. package/assets/skills-en/observability/thread-tracing/SKILL.md +63 -0
  89. package/assets/skills-en/phases/check-quality/SKILL.md +103 -0
  90. package/assets/skills-en/phases/go-implementation/SKILL.md +78 -0
  91. package/assets/skills-en/phases/goal-definition/SKILL.md +69 -0
  92. package/assets/skills-en/phases/master-metrics/SKILL.md +72 -0
  93. package/assets/skills-en/phases/plan-specification/SKILL.md +67 -0
  94. package/assets/skills-en/phases/security-remediation/SKILL.md +70 -0
  95. package/assets/skills-en/phases/ship-release/SKILL.md +71 -0
  96. package/assets/skills-en/reviewers/code-reviewer/SKILL.md +61 -0
  97. package/assets/skills-en/reviewers/security-auditor/SKILL.md +93 -0
  98. package/assets/skills-en/reviewers/test-engineer/SKILL.md +66 -0
  99. package/assets/skills-en/reviewers/web-performance-auditor/SKILL.md +63 -0
  100. package/dist/adapters/claude-bg.js +123 -9
  101. package/dist/adapters/codex-controller-worker.js +12 -5
  102. package/dist/adapters/codex-events.js +16 -4
  103. package/dist/adapters/codex.js +3 -1
  104. package/dist/adapters/grok-events.js +227 -0
  105. package/dist/adapters/grok-mcp-servidor.js +612 -0
  106. package/dist/adapters/grok-mcp.js +178 -0
  107. package/dist/adapters/grok.js +686 -0
  108. package/dist/agendador-cpu.js +92 -0
  109. package/dist/atualizar-branch.js +245 -0
  110. package/dist/auditoria.js +13 -11
  111. package/dist/auditrun.js +51 -46
  112. package/dist/autorizacao-duravel.js +327 -0
  113. package/dist/board.js +94 -40
  114. package/dist/branch-de-estado.js +23 -10
  115. package/dist/busca-semantica.js +7 -5
  116. package/dist/catalogo.js +2 -1
  117. package/dist/changelog-reconciliacao.js +290 -0
  118. package/dist/check-agil.js +528 -0
  119. package/dist/check-modo.js +41 -0
  120. package/dist/check-paralelo.js +238 -0
  121. package/dist/ci.js +248 -45
  122. package/dist/ciclos.js +8 -6
  123. package/dist/claim-lint.js +12 -8
  124. package/dist/claims-conferir.js +47 -39
  125. package/dist/claims.js +10 -9
  126. package/dist/company-brain-client.js +10 -3
  127. package/dist/company-brain-migration.js +11 -5
  128. package/dist/conducao-batida.js +759 -0
  129. package/dist/conducao-sentinela.js +25 -8
  130. package/dist/conducao.js +89 -29
  131. package/dist/conhecimento-consulta.js +270 -0
  132. package/dist/conhecimento.js +111 -87
  133. package/dist/conta-esgotada.js +127 -0
  134. package/dist/dado-privado.js +816 -0
  135. package/dist/decisao-autonoma.js +5 -4
  136. package/dist/demo.js +14 -12
  137. package/dist/dependencias-da-worktree.js +25 -0
  138. package/dist/divida.js +38 -36
  139. package/dist/docs.js +89 -89
  140. package/dist/doctor-forja.js +343 -0
  141. package/dist/doctor-pulse-cron.js +113 -1
  142. package/dist/doctor.js +164 -6
  143. package/dist/dsn-da-memoria.js +417 -0
  144. package/dist/entrega-por-mod.js +503 -0
  145. package/dist/entrega-pr.js +154 -54
  146. package/dist/esgotamentos.js +13 -11
  147. package/dist/estado-thread.js +21 -18
  148. package/dist/evalrunner.js +35 -32
  149. package/dist/fabrica-batida.js +34 -21
  150. package/dist/fabrica-estado.js +33 -21
  151. package/dist/fix.js +21 -19
  152. package/dist/forja/adaptador.js +6 -0
  153. package/dist/forja/ambiente-de-ci.js +27 -0
  154. package/dist/forja/api.js +256 -0
  155. package/dist/forja/cache.js +89 -0
  156. package/dist/forja/cli.js +60 -0
  157. package/dist/forja/contrato.js +9 -0
  158. package/dist/forja/endereco.js +43 -0
  159. package/dist/forja/estado.js +22 -0
  160. package/dist/forja/executor.js +73 -0
  161. package/dist/forja/github/ci.js +95 -0
  162. package/dist/forja/github/dono-da-rede.js +18 -0
  163. package/dist/forja/github/metadados.js +15 -0
  164. package/dist/forja/github/pedidos.js +200 -0
  165. package/dist/forja/gitlab/ci.js +141 -0
  166. package/dist/forja/gitlab/consultas.js +59 -0
  167. package/dist/forja/gitlab/dono-da-rede.js +43 -0
  168. package/dist/forja/gitlab/metadados.js +15 -0
  169. package/dist/forja/gitlab/pedidos-abertos.js +158 -0
  170. package/dist/forja/gitlab/pedidos.js +141 -0
  171. package/dist/forja/produtor.js +32 -0
  172. package/dist/forja/registro.js +31 -0
  173. package/dist/forja/resolver.js +121 -0
  174. package/dist/forja/texto.js +13 -0
  175. package/dist/forja/versao.js +71 -0
  176. package/dist/forja.js +123 -80
  177. package/dist/handoff.js +23 -13
  178. package/dist/hitl-contract.js +46 -43
  179. package/dist/hitl-curto.js +37 -25
  180. package/dist/hitl-gates.js +13 -11
  181. package/dist/hitl-local-receipt.js +51 -0
  182. package/dist/hitl-local.js +11 -2
  183. package/dist/hitl-lock.js +5 -11
  184. package/dist/hitl-public-receipt.js +11 -0
  185. package/dist/hitl-resumo.js +44 -1
  186. package/dist/hitl-sessions.js +62 -9
  187. package/dist/hitl-texto-livre.js +7 -4
  188. package/dist/hitl.js +34 -31
  189. package/dist/horario.js +20 -1
  190. package/dist/hosts.js +104 -134
  191. package/dist/index.js +864 -554
  192. package/dist/indice-vetorial.js +14 -12
  193. package/dist/init.js +11 -2
  194. package/dist/integracoes-locais.js +4 -0
  195. package/dist/intelligence-graph-cli.js +123 -69
  196. package/dist/intelligence-graph-query.js +37 -22
  197. package/dist/leases.js +19 -18
  198. package/dist/ledger-stats.js +17 -16
  199. package/dist/ledger.js +31 -1
  200. package/dist/licoes.js +12 -11
  201. package/dist/locale-accounts.js +228 -0
  202. package/dist/locale-adapter.js +291 -0
  203. package/dist/locale-ajuda.js +86 -10
  204. package/dist/locale-audit.js +199 -0
  205. package/dist/locale-claims.js +213 -0
  206. package/dist/locale-conducao.js +138 -0
  207. package/dist/locale-conhecimento.js +181 -0
  208. package/dist/locale-decisao.js +205 -0
  209. package/dist/locale-docs.js +230 -0
  210. package/dist/locale-estado.js +101 -0
  211. package/dist/locale-forja.js +288 -0
  212. package/dist/locale-gerados.js +5 -0
  213. package/dist/locale-grafo.js +129 -0
  214. package/dist/locale-master.js +83 -0
  215. package/dist/locale-network.js +524 -0
  216. package/dist/locale-operacao-restante.js +931 -0
  217. package/dist/locale-projetos.js +100 -0
  218. package/dist/locale-prompts.js +194 -0
  219. package/dist/locale-pulse.js +140 -2
  220. package/dist/locale-reservas.js +87 -0
  221. package/dist/locale-retry.js +183 -6
  222. package/dist/locale-security-scan.js +69 -0
  223. package/dist/locale-setup.js +192 -0
  224. package/dist/locale-ship.js +177 -66
  225. package/dist/locale-thread.js +149 -5
  226. package/dist/locale-verify.js +80 -4
  227. package/dist/locale-worktree.js +246 -0
  228. package/dist/locale.js +553 -9
  229. package/dist/maestro-runtime.js +22 -3
  230. package/dist/manifest.js +293 -12
  231. package/dist/maquina-ociosa.js +211 -0
  232. package/dist/maquina.js +10 -5
  233. package/dist/master-nota-local.js +200 -0
  234. package/dist/master-nota.js +10 -2
  235. package/dist/master.js +118 -41
  236. package/dist/mcp-conhecimento.js +52 -0
  237. package/dist/mcp-experiencia.js +26 -5
  238. package/dist/mcp-install.js +90 -15
  239. package/dist/mcp-noctum.js +101 -0
  240. package/dist/mcp-server.js +116 -22
  241. package/dist/mcp-ship-transporte.js +977 -0
  242. package/dist/mcp-ship.js +68 -24
  243. package/dist/mcp-transportes.js +17 -0
  244. package/dist/memoria.js +63 -58
  245. package/dist/merge-da-entrega.js +85 -0
  246. package/dist/modos-migracao.js +8 -6
  247. package/dist/modos.js +17 -18
  248. package/dist/network-roadmap.js +94 -54
  249. package/dist/noctum-aceite.js +929 -0
  250. package/dist/noctum-arco.js +540 -33
  251. package/dist/noctum-capacidade.js +3 -3
  252. package/dist/noctum-changelog.js +399 -0
  253. package/dist/noctum-cli.js +539 -0
  254. package/dist/noctum-coordenacao.js +1225 -0
  255. package/dist/noctum-entrega.js +1217 -0
  256. package/dist/noctum-envelope.js +253 -17
  257. package/dist/noctum-execucao.js +100 -24
  258. package/dist/noctum-fechamento.js +416 -0
  259. package/dist/noctum-fila.js +49 -10
  260. package/dist/noctum-guarda.js +333 -0
  261. package/dist/noctum-integracao.js +507 -0
  262. package/dist/noctum-maquinas.js +387 -0
  263. package/dist/noctum-medicao.js +450 -0
  264. package/dist/noctum-modo.js +390 -0
  265. package/dist/noctum-pergunta-canal.js +96 -0
  266. package/dist/noctum-pergunta.js +115 -0
  267. package/dist/noctum-provas.js +838 -0
  268. package/dist/noctum-reconciliacao.js +594 -0
  269. package/dist/noctum-rede.js +102 -0
  270. package/dist/noctum-registro.js +215 -0
  271. package/dist/noctum-relatorio.js +861 -0
  272. package/dist/noctum-resposta.js +207 -0
  273. package/dist/noctum-retomada-rede.js +71 -0
  274. package/dist/noctum-sessoes-em-voo.js +93 -0
  275. package/dist/noctum-supervisao.js +649 -0
  276. package/dist/noctum-terminal.js +459 -0
  277. package/dist/noctum-transporte.js +655 -0
  278. package/dist/noctum-vigia.js +679 -0
  279. package/dist/ocupacao.js +3 -1
  280. package/dist/onboarding.js +16 -5
  281. package/dist/orkmind.js +39 -23
  282. package/dist/orquestracao.js +31 -35
  283. package/dist/parado-no-condutor.js +160 -139
  284. package/dist/phase.js +96 -42
  285. package/dist/policies.js +33 -0
  286. package/dist/postura-local.js +11 -6
  287. package/dist/preflight.js +2 -1
  288. package/dist/procedencia.js +9 -14
  289. package/dist/projeto-alvo.js +59 -20
  290. package/dist/prompts.js +136 -42
  291. package/dist/prova-minima.js +5 -6
  292. package/dist/pulse-cadencia.js +5 -4
  293. package/dist/pulse-consentimento.js +53 -1
  294. package/dist/pulse-delivery.js +143 -27
  295. package/dist/pulse-resposta.js +368 -4
  296. package/dist/pulse.js +136 -3
  297. package/dist/ratelimit.js +13 -4
  298. package/dist/recall.js +14 -13
  299. package/dist/rede-adesao.js +13 -8
  300. package/dist/rede-forja.js +108 -24
  301. package/dist/rede-status.js +63 -45
  302. package/dist/rede.js +86 -46
  303. package/dist/retomada-checkpoint.js +101 -0
  304. package/dist/retry.js +326 -78
  305. package/dist/roadmap-reservas.js +32 -32
  306. package/dist/runtime-ambiente.js +5 -0
  307. package/dist/runtime-context.js +279 -85
  308. package/dist/runtime-escada.js +429 -0
  309. package/dist/runtime-profiles.js +130 -63
  310. package/dist/runtimes.js +42 -5
  311. package/dist/saida-segura.js +3 -2
  312. package/dist/security-scan/agil.js +366 -0
  313. package/dist/security-scan/cli.js +76 -0
  314. package/dist/security-scan/compare.js +149 -0
  315. package/dist/security-scan/config.js +93 -0
  316. package/dist/security-scan/detect.js +97 -0
  317. package/dist/security-scan/executor.js +196 -0
  318. package/dist/security-scan/guard.js +180 -0
  319. package/dist/security-scan/imagem-de-lint.js +96 -0
  320. package/dist/security-scan/normalize/actionlint.js +12 -0
  321. package/dist/security-scan/normalize/eslint.js +12 -0
  322. package/dist/security-scan/normalize/gitleaks.js +13 -0
  323. package/dist/security-scan/normalize/golangci-lint.js +13 -0
  324. package/dist/security-scan/normalize/govulncheck.js +36 -0
  325. package/dist/security-scan/normalize/index.js +25 -0
  326. package/dist/security-scan/normalize/npm-audit.js +30 -0
  327. package/dist/security-scan/normalize/osv-scanner.js +32 -0
  328. package/dist/security-scan/normalize/oxlint.js +51 -0
  329. package/dist/security-scan/normalize/pip-audit.js +16 -0
  330. package/dist/security-scan/normalize/ruff.js +12 -0
  331. package/dist/security-scan/normalize/semgrep.js +20 -0
  332. package/dist/security-scan/normalize/trivy.js +33 -0
  333. package/dist/security-scan/normalize/util.js +204 -0
  334. package/dist/security-scan/normalize/zizmor.js +19 -0
  335. package/dist/security-scan/report.js +156 -0
  336. package/dist/security-scan/run.js +448 -0
  337. package/dist/security-scan/tools.js +62 -0
  338. package/dist/security-scan/trabalhador.js +74 -0
  339. package/dist/security-scan/types.js +15 -0
  340. package/dist/session-events.js +19 -2
  341. package/dist/session-watcher-claude.js +74 -19
  342. package/dist/session-watcher-grok.js +425 -0
  343. package/dist/session-watcher.js +174 -13
  344. package/dist/sessoes-contas.js +42 -5
  345. package/dist/sessoes-inventario.js +1 -1
  346. package/dist/sessoes.js +25 -23
  347. package/dist/setup.js +67 -66
  348. package/dist/ship-publicar.js +263 -92
  349. package/dist/ship.js +160 -17
  350. package/dist/superficie.js +14 -11
  351. package/dist/thread-close.js +9 -9
  352. package/dist/thread.js +121 -18
  353. package/dist/tokens.js +15 -11
  354. package/dist/trabalho-thread.js +297 -0
  355. package/dist/transporte-do-canal.js +189 -0
  356. package/dist/types.js +12 -1
  357. package/dist/util.js +5 -2
  358. package/dist/varredura-acoes.js +500 -0
  359. package/dist/verify.js +491 -32
  360. package/dist/worktree-local.js +12 -10
  361. package/dist/worktree.js +81 -85
  362. package/eval/casos/check-quality.json +77 -0
  363. package/eval/casos/security-auditor.json +148 -0
  364. package/eval/casos/security-remediation.json +161 -0
  365. package/eval/fixtures/security-scan/README.md +8 -0
  366. package/eval/fixtures/security-scan/go/.github/workflows/ci.yml +12 -0
  367. package/eval/fixtures/security-scan/go/Dockerfile +4 -0
  368. package/eval/fixtures/security-scan/go/README.md +4 -0
  369. package/eval/fixtures/security-scan/go/config.go +4 -0
  370. package/eval/fixtures/security-scan/go/go.mod +5 -0
  371. package/eval/fixtures/security-scan/go/go.sum +2 -0
  372. package/eval/fixtures/security-scan/go/main.go +31 -0
  373. package/eval/fixtures/security-scan/js/.github/workflows/ci.yml +12 -0
  374. package/eval/fixtures/security-scan/js/Dockerfile +3 -0
  375. package/eval/fixtures/security-scan/js/README.md +4 -0
  376. package/eval/fixtures/security-scan/js/package-lock.json +844 -0
  377. package/eval/fixtures/security-scan/js/package.json +10 -0
  378. package/eval/fixtures/security-scan/js/src/config.js +4 -0
  379. package/eval/fixtures/security-scan/js/src/server.js +17 -0
  380. package/eval/fixtures/security-scan/js/src/util.ts +4 -0
  381. package/eval/fixtures/security-scan/js/tsconfig.json +4 -0
  382. package/eval/fixtures/security-scan/python/.github/workflows/ci.yml +12 -0
  383. package/eval/fixtures/security-scan/python/Dockerfile +3 -0
  384. package/eval/fixtures/security-scan/python/README.md +4 -0
  385. package/eval/fixtures/security-scan/python/app/__init__.py +0 -0
  386. package/eval/fixtures/security-scan/python/app/config.py +2 -0
  387. package/eval/fixtures/security-scan/python/app/main.py +19 -0
  388. package/eval/fixtures/security-scan/python/requirements.txt +1 -0
  389. package/package.json +10 -5
  390. package/references/security-checklist.md +3 -3
  391. package/skills/README.md +1 -0
  392. package/skills/core/orkastery-bootstrap/SKILL.md +2 -2
  393. package/skills/phases/check-quality/SKILL.md +16 -0
  394. package/skills/phases/go-implementation/SKILL.md +6 -0
  395. package/skills/phases/security-remediation/SKILL.md +70 -0
  396. package/skills/reviewers/security-auditor/SKILL.md +25 -0
@@ -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
+ }
@@ -0,0 +1,494 @@
1
+ // O Ritual do MASTER do Orkastery (RM-059, fatia F8; ficha M8 do plano dos mods). Na sessao do dono, um painel com as
2
+ // entregas que esperam a nota do dono, lidas do nucleo pelo retrato da ponte (`$.ork.retrato`, secao `master`), e o
3
+ // replay da prova de cada uma, com as claims pelo `ork_claims_list`. O botao "Dar a nota" so pede ao nucleo o dialogo
4
+ // nativo (`ork_request_master_note`, com o `threadId` e nada mais), e a reserva pelo Telegram so pede o codigo
5
+ // (`ork master pedir`), no clique do dono. Nunca le `.orkastery/` por conta propria e nunca da a nota pelo dono. A
6
+ // logica fica nos modulos puros ao lado; aqui so os ganchos e as chamadas ao `$`, sempre escritas como
7
+ // `x.noun.metodo(...)`, como o validador exige.
8
+ import type { EngineInterface, Register } from 'claude-code'
9
+ import type { RitualMostrado, RitualNota, RitualProva, RitualQuadro, RitualReserva } from '../types'
10
+ import {
11
+ argsDaNota, argvDaReserva, lerReserva, notaDoErro, PRAZO_DA_NOTA_MS, PRAZO_DA_RESERVA_MS, resultadoDaNota, soltarAbrindo,
12
+ } from './nota'
13
+ import { desenharRitual, linhasDoPainel, mostradosVisiveis } from './replay'
14
+ import {
15
+ argsDasClaims, avisosDasLacunas, claimsDaProva, entregasDaJanela, exigirThread, jsonDoMcp, paginaDoMaster, plural, QUADRO_VAZIO,
16
+ semNotaDada, SERVIDOR, TETO_DE_PAGINAS, textoDaFaixa, threadDoLedger, threadsSemLeitura, umaLinha,
17
+ } from './ritual'
18
+
19
+ const MOD = 'ork-ritual'
20
+ const PAINEL = 'ritual'
21
+ const TITULO = 'Ritual do MASTER'
22
+ const QUADRO = { plugin: 'ork-ritual', key: 'quadro' } as const
23
+ const FOCO = { plugin: 'ork-ritual', key: 'foco' } as const
24
+ const PASSO = { plugin: 'ork-ritual', key: 'passo' } as const
25
+ const PROVA = { plugin: 'ork-ritual', key: 'prova' } as const
26
+ const NOTAS = { plugin: 'ork-ritual', key: 'notas' } as const
27
+ const RESERVAS = { plugin: 'ork-ritual', key: 'reservas' } as const
28
+ /** O tique: idade das entregas e registro do mostrado. */
29
+ const PERIODO_DO_TIQUE_MS = 60000
30
+ /** As entregas sao relidas por idade: a cada 5 min com o painel a vista, a cada 15 min escondido. */
31
+ const IDADE_A_VISTA_MS = 5 * 60000
32
+ const IDADE_ESCONDIDO_MS = 15 * 60000
33
+ /**
34
+ * Entre duas leituras que o dono nao pediu (ledger que mudou, idade), no minimo isto com o painel a vista; escondido, a
35
+ * idade de 15 min. Cada leitura rele todas as paginas da secao `master` do retrato, e cada pagina le os ledgers de todas
36
+ * as threads: a faixa nao precisa de mais que isso, e a entrega anda em minutos, nao em segundos.
37
+ */
38
+ const INTERVALO_A_VISTA_MS = 60000
39
+ /** Os ledgers que mudam juntos entram numa leitura so. */
40
+ const ESPERA_DO_LOTE_MS = 5000
41
+ /** Teto de cada chamada ao nucleo (uma pagina do retrato ou as claims de uma thread). */
42
+ const PRAZO_DA_LEITURA_MS = 20000
43
+ /** O servidor do nucleo pode conectar depois do inicio da sessao: o papel e conferido de novo nestas esperas. */
44
+ const ESPERAS_DO_PAPEL_MS = [3000, 10000, 30000]
45
+ const ESPERA_LONGA_DO_PAPEL_MS = 300000
46
+
47
+ // O que o modulo guarda entre ganchos. O estado em `$.state` (quadro, foco, passo e prova) e o que o desenho le; aqui
48
+ // ficam a copia dele, sempre gravada inteira por `$.state.set` sem versao, uma gravacao por mudanca (licao da Sentinela
49
+ // e da Pista), e o que nao se desenha. Uma recarga do modulo zera tudo, e o `session.start` dispara de novo nela.
50
+ let ativa = false
51
+ let desistiu = false
52
+ let ciclo: { cancel: () => void } | undefined
53
+ let lote: { cancel: () => void } | undefined
54
+ let lendo: Promise<void> | undefined
55
+ let pendente = false
56
+ let tentadaEm = 0
57
+ let quadro: RitualQuadro = QUADRO_VAZIO
58
+ let foco: string | null = null
59
+ let passo = 0
60
+ let prova: RitualProva | null = null
61
+ /** A leitura das claims em curso e de qual thread. */
62
+ let lendoProva: Promise<void> | undefined
63
+ let lendoProvaDe: string | null = null
64
+ /** O "Dar a nota" e a reserva de cada entrega, e as threads com um dialogo ou um pedido rodando no nucleo agora. */
65
+ let notas: Record<string, RitualNota> = {}
66
+ let reservas: Record<string, RitualReserva> = {}
67
+ const emDialogo = new Set<string>()
68
+ const pedindo = new Set<string>()
69
+ /** O que o desenho pos na janela de rolagem e ainda nao foi ao registro da ponte. */
70
+ const vistosNoDesenho = new Map<string, RitualMostrado>()
71
+ /** O que ja foi ao registro nesta sessao, por chave e texto. */
72
+ const jaMostrado = new Set<string>()
73
+ /** O ultimo texto publicado na faixa; null antes da primeira publicacao. */
74
+ let ultimaFaixa: string | undefined | null = null
75
+ /** A falha de um trabalho em segundo plano que nem o quadro conseguiu guardar. */
76
+ let problemaSemAviso: string | null = null
77
+
78
+ function mensagem(erro: unknown): string {
79
+ return umaLinha(erro, 200)
80
+ }
81
+
82
+ /** A leitura com teto proprio: o relogio da sessao recusa a espera que passar dele. */
83
+ function comPrazo<T>($: EngineInterface, leitura: Promise<T>, ms = PRAZO_DA_LEITURA_MS): Promise<T> {
84
+ return new Promise<T>((resolver, recusar) => {
85
+ const prazo = $.clock.after(ms, () => recusar(new Error(`a leitura passou de ${Math.round(ms / 1000)} s`)))
86
+ leitura.then(v => { prazo.cancel(); resolver(v) }, e => { prazo.cancel(); recusar(e) })
87
+ })
88
+ }
89
+
90
+ /**
91
+ * Trabalho em segundo plano (o tique, o lote, os gestos do painel): a falha vai ao quadro; sem como gravar (a sessao
92
+ * acabou ou o estado recusou), ela fica guardada aqui e o proximo desenho a mostra. O `.catch` nao depende do `$`.
93
+ */
94
+ function emSegundoPlano($: EngineInterface, trabalho: Promise<unknown>, oQue: string): void {
95
+ trabalho.catch(async erro => {
96
+ const texto = `${oQue} parou: ${mensagem(erro)}`
97
+ try {
98
+ await gravarQuadro($, { ...quadro, erro: texto })
99
+ } catch (erroDoAviso) {
100
+ problemaSemAviso = `${texto} (o aviso também falhou: ${mensagem(erroDoAviso)})`
101
+ }
102
+ })
103
+ }
104
+
105
+ async function gravarQuadro($: EngineInterface, novo: RitualQuadro): Promise<void> {
106
+ quadro = novo
107
+ await $.state.set(QUADRO, novo)
108
+ }
109
+
110
+ /**
111
+ * O papel pela ponte. `null` so quando o `$.ork` nao existe (a ponte nao carregou); uma falha da propria ponte conta
112
+ * como dormente e o papel e conferido de novo depois.
113
+ */
114
+ async function papelDaSessao($: EngineInterface): Promise<string | null> {
115
+ try {
116
+ return String(await $.ork.papel())
117
+ } catch (erro) {
118
+ return erro instanceof TypeError ? null : 'dormente'
119
+ }
120
+ }
121
+
122
+ /** Liga o Ritual na sessao do dono: o `/nota`, o tique e a primeira leitura. */
123
+ async function ativar($: EngineInterface): Promise<void> {
124
+ if (ativa) return
125
+ ativa = true
126
+ try {
127
+ await $.command.register({ name: 'nota', description: 'Abre o Ritual do MASTER: as entregas que esperam a sua nota, com a prova de cada uma', immediate: true })
128
+ } catch (erro) {
129
+ // Sem o /nota o painel nao abre: o Ritual volta a dormente e tenta de novo na espera longa.
130
+ ativa = false
131
+ $.clock.after(ESPERA_LONGA_DO_PAPEL_MS, () => { emSegundoPlano($, conferirPapel($, ESPERAS_DO_PAPEL_MS.length), 'a conferência do papel') })
132
+ throw erro
133
+ }
134
+ quadro = (await $.state.get(QUADRO)).value ?? QUADRO_VAZIO
135
+ foco = (await $.state.get(FOCO)).value ?? null
136
+ passo = (await $.state.get(PASSO)).value ?? 0
137
+ prova = (await $.state.get(PROVA)).value ?? null
138
+ // Depois de uma recarga do modulo, nenhum dialogo nem pedido esta rodando: o que ficou em curso volta a ter o botao.
139
+ notas = soltarAbrindo((await $.state.get(NOTAS)).value ?? {})
140
+ await $.state.set(NOTAS, notas)
141
+ const guardadas = (await $.state.get(RESERVAS)).value ?? {}
142
+ reservas = Object.fromEntries(Object.entries(guardadas).map(([t, r]) => [t, r.estado === 'pedindo'
143
+ ? { ...r, estado: 'erro' as const, texto: 'o pedido pelo Telegram foi interrompido por uma recarga; peça de novo' } : r]))
144
+ await $.state.set(RESERVAS, reservas)
145
+ ciclo?.cancel()
146
+ ciclo = $.clock.every(PERIODO_DO_TIQUE_MS, () => { emSegundoPlano($, tique($), 'o tique do Ritual') })
147
+ agendar($, 1)
148
+ }
149
+
150
+ /** Confere o papel: liga no dono, desiste na fase ou sem a ponte, e tenta de novo enquanto dormente. */
151
+ async function conferirPapel($: EngineInterface, tentativa: number): Promise<void> {
152
+ if (ativa || desistiu) return
153
+ const papel = await papelDaSessao($)
154
+ if (papel === 'dono') return ativar($)
155
+ if (papel === 'fase' || papel === null) {
156
+ desistiu = true
157
+ if (papel === null) $.ui.toast('ritual: a ponte ork-ponte não carregou; o Ritual do MASTER fica desligado nesta sessão')
158
+ return
159
+ }
160
+ const espera = ESPERAS_DO_PAPEL_MS[tentativa] ?? ESPERA_LONGA_DO_PAPEL_MS
161
+ $.clock.after(espera, () => { emSegundoPlano($, conferirPapel($, tentativa + 1), 'a conferência do papel') })
162
+ }
163
+
164
+ /** Uma leitura daqui a pouco, uma por vez: os ledgers que mudam juntos entram nela. */
165
+ function agendar($: EngineInterface, ms = ESPERA_DO_LOTE_MS): void {
166
+ if (lote) return
167
+ lote = $.clock.after(Math.max(1, ms), () => {
168
+ lote = undefined
169
+ emSegundoPlano($, atualizar($), 'a leitura das entregas')
170
+ })
171
+ }
172
+
173
+ /** O painel do Ritual esta posto e a vista do dono. */
174
+ async function painelAVista($: EngineInterface): Promise<boolean> {
175
+ const paineis = await $.ui.panes().catch(() => [])
176
+ return paineis.some(p => p.id === PAINEL && p.isPlaced && p.isShown)
177
+ }
178
+
179
+ /**
180
+ * Uma leitura por vez; pedido que chega no meio volta a rodar logo depois. Fora do pedido do dono (`forcar`), a leitura
181
+ * respeita o intervalo: com o painel a vista, volta a ser agendada para quando ele vencer; escondido, espera o tique da
182
+ * idade.
183
+ */
184
+ async function atualizar($: EngineInterface, forcar = false): Promise<void> {
185
+ if (!ativa) return
186
+ const agora = await $.clock.now()
187
+ if (!forcar && tentadaEm > 0) {
188
+ const aVista = await painelAVista($)
189
+ const espera = (aVista ? INTERVALO_A_VISTA_MS : IDADE_ESCONDIDO_MS) - (agora - tentadaEm)
190
+ if (espera > 0) {
191
+ if (aVista) agendar($, espera)
192
+ return
193
+ }
194
+ }
195
+ if (lendo) {
196
+ pendente = true
197
+ return lendo
198
+ }
199
+ lendo = (async () => {
200
+ try {
201
+ do {
202
+ pendente = false
203
+ await lerUmaVez($)
204
+ } while (pendente)
205
+ } finally {
206
+ lendo = undefined
207
+ }
208
+ })()
209
+ return lendo
210
+ }
211
+
212
+ /**
213
+ * As entregas pelo retrato da ponte, pagina a pagina da secao `master` (D2): a que espera a nota do dono, na janela de
214
+ * dias, sem a que teve a nota registrada nesta sessao (a pagina guardada pela ponte pode ainda lista-la). A falha fica
215
+ * no quadro, com a leitura anterior.
216
+ */
217
+ async function lerUmaVez($: EngineInterface): Promise<void> {
218
+ tentadaEm = await $.clock.now()
219
+ const itens: unknown[] = []
220
+ const lacunas: string[] = []
221
+ const avisos: string[] = []
222
+ let offset = 0
223
+ try {
224
+ for (let pagina = 0; ; pagina++) {
225
+ if (pagina >= TETO_DE_PAGINAS) {
226
+ avisos.push(`a seção master passou de ${TETO_DE_PAGINAS} páginas; o resto ficou de fora`)
227
+ break
228
+ }
229
+ const p = paginaDoMaster(await comPrazo($, $.ork.retrato({ secao: 'master', offset })))
230
+ itens.push(...p.itens)
231
+ lacunas.push(...p.lacunas)
232
+ if (p.proxima === null || p.proxima <= offset) break
233
+ offset = p.proxima
234
+ }
235
+ } catch (erro) {
236
+ await gravarQuadro($, { ...quadro, erro: mensagem(erro) })
237
+ await publicarFaixa($)
238
+ return
239
+ }
240
+ const agora = await $.clock.now()
241
+ const { entregas, fora, alemDoTeto } = entregasDaJanela(itens, agora)
242
+ avisos.unshift(...avisosDasLacunas(lacunas))
243
+ if (alemDoTeto) avisos.push(`${plural(alemDoTeto, 'entrega além do teto do painel ficou', 'entregas além do teto do painel ficaram')} de fora (ork master)`)
244
+ await gravarQuadro($, { entregas: semNotaDada(entregas, notas), lidoEm: agora, erro: null, avisos, foraDaJanela: fora,
245
+ semLeitura: threadsSemLeitura(lacunas) })
246
+ await publicarFaixa($)
247
+ await registrarMostrados($)
248
+ }
249
+
250
+ /** As claims da thread em foco pelo `ork_claims_list`, uma leitura por vez; a da thread que saiu do foco e descartada. */
251
+ async function lerProva($: EngineInterface, thread: string): Promise<void> {
252
+ if (lendoProva) {
253
+ if (lendoProvaDe === thread) return lendoProva
254
+ await lendoProva.catch(() => undefined)
255
+ }
256
+ lendoProvaDe = thread
257
+ lendoProva = (async () => {
258
+ let nova: RitualProva
259
+ try {
260
+ const { claims, retiradas } = claimsDaProva(jsonDoMcp(await comPrazo($, $.mcp.call(SERVIDOR, 'ork_claims_list', argsDasClaims(thread)))))
261
+ nova = { thread, claims, retiradas, lidoEm: await $.clock.now(), erro: null }
262
+ } catch (erro) {
263
+ const anterior = prova?.thread === thread ? prova : null
264
+ nova = { thread, claims: anterior?.claims ?? [], retiradas: anterior?.retiradas ?? 0, lidoEm: await $.clock.now(), erro: mensagem(erro) }
265
+ }
266
+ if (foco !== thread) return
267
+ prova = nova
268
+ passo = Math.min(passo, Math.max(0, nova.claims.length - 1))
269
+ await $.state.set(PROVA, prova)
270
+ await $.state.set(PASSO, passo)
271
+ })()
272
+ try {
273
+ await lendoProva
274
+ } finally {
275
+ lendoProva = undefined
276
+ lendoProvaDe = null
277
+ }
278
+ }
279
+
280
+ /** Abre o replay de uma thread: o foco e o passo agora; as claims, em segundo plano. */
281
+ async function focar($: EngineInterface, thread: string): Promise<void> {
282
+ if (foco !== thread || prova?.thread !== thread) {
283
+ foco = thread
284
+ passo = 0
285
+ prova = null
286
+ await $.state.set(FOCO, foco)
287
+ await $.state.set(PASSO, passo)
288
+ await $.state.set(PROVA, prova)
289
+ }
290
+ emSegundoPlano($, lerProva($, thread), 'a leitura das claims')
291
+ }
292
+
293
+ /** Fecha o replay: volta a lista das entregas. */
294
+ async function soltarFoco($: EngineInterface): Promise<void> {
295
+ if (foco === null) return
296
+ foco = null
297
+ passo = 0
298
+ prova = null
299
+ await $.state.set(FOCO, foco)
300
+ await $.state.set(PASSO, passo)
301
+ await $.state.set(PROVA, prova)
302
+ }
303
+
304
+ /** Anda uma claim para tras ou para frente no replay, dentro da lista lida. */
305
+ async function andar($: EngineInterface, delta: number): Promise<void> {
306
+ if (!prova || prova.thread !== foco || !prova.claims.length) return
307
+ const novo = Math.min(Math.max(0, passo + delta), prova.claims.length - 1)
308
+ if (novo === passo) return
309
+ passo = novo
310
+ await $.state.set(PASSO, passo)
311
+ }
312
+
313
+ async function gravarNota($: EngineInterface, thread: string, n: RitualNota): Promise<void> {
314
+ notas = { ...notas, [thread]: n }
315
+ await $.state.set(NOTAS, notas)
316
+ }
317
+
318
+ async function gravarReserva($: EngineInterface, thread: string, r: RitualReserva): Promise<void> {
319
+ reservas = { ...reservas, [thread]: r }
320
+ await $.state.set(RESERVAS, reservas)
321
+ }
322
+
323
+ /**
324
+ * O "Dar a nota" (T8.3, DD2): um dialogo por thread, em segundo plano. A chamada leva exatamente o `threadId`; quem da a
325
+ * nota e o porque e o dono, no dialogo nativo do host, e quem grava e o nucleo. A entrega anotada sai da lista e da
326
+ * faixa assim que o nucleo responde, e a releitura logo depois confirma.
327
+ */
328
+ async function darNota($: EngineInterface, thread: string): Promise<void> {
329
+ if (emDialogo.has(thread)) return
330
+ // Marcado antes de qualquer espera: dois toques ao mesmo tempo abrem um dialogo so.
331
+ emDialogo.add(thread)
332
+ try {
333
+ exigirThread(thread)
334
+ await gravarNota($, thread, { estado: 'abrindo', texto: '', em: await $.clock.now() })
335
+ let n: RitualNota
336
+ try {
337
+ n = resultadoDaNota(jsonDoMcp(await comPrazo($, $.mcp.call(SERVIDOR, 'ork_request_master_note', argsDaNota(thread)), PRAZO_DA_NOTA_MS)),
338
+ thread, await $.clock.now())
339
+ } catch (erro) {
340
+ n = notaDoErro(erro, await $.clock.now())
341
+ }
342
+ await gravarNota($, thread, n)
343
+ if (n.estado === 'registrada' || n.estado === 'repetida') {
344
+ await gravarQuadro($, { ...quadro, entregas: semNotaDada(quadro.entregas, notas) })
345
+ await publicarFaixa($)
346
+ }
347
+ } finally {
348
+ emDialogo.delete(thread)
349
+ }
350
+ await atualizar($, true)
351
+ }
352
+
353
+ /**
354
+ * A reserva pelo Telegram (T8.3, D7): so no clique do dono, o pedido de nota do nucleo (`ork master pedir`), que guarda
355
+ * o pedido e devolve o codigo. O mod nunca roda `ork master` com nota no argv.
356
+ */
357
+ async function pedirTelegram($: EngineInterface, thread: string): Promise<void> {
358
+ if (pedindo.has(thread)) return
359
+ pedindo.add(thread)
360
+ try {
361
+ exigirThread(thread)
362
+ await gravarReserva($, thread, { estado: 'pedindo', texto: '', codigo: null, em: await $.clock.now() })
363
+ let r: RitualReserva
364
+ try {
365
+ r = lerReserva(await $.process.run(argvDaReserva(thread), { timeoutMs: PRAZO_DA_RESERVA_MS }), thread, await $.clock.now())
366
+ } catch (erro) {
367
+ r = { estado: 'erro', texto: `o pedido pelo Telegram falhou: ${mensagem(erro)}`, codigo: null, em: await $.clock.now() }
368
+ }
369
+ await gravarReserva($, thread, r)
370
+ } finally {
371
+ pedindo.delete(thread)
372
+ }
373
+ await registrarMostrados($)
374
+ }
375
+
376
+ /** O tique: a idade das entregas e o registro do que o painel mostrou. */
377
+ async function tique($: EngineInterface): Promise<void> {
378
+ if (!ativa) return
379
+ const agora = await $.clock.now()
380
+ const aVista = await painelAVista($)
381
+ const idade = quadro.lidoEm === null ? Number.POSITIVE_INFINITY : agora - quadro.lidoEm
382
+ if (idade >= (aVista ? IDADE_A_VISTA_MS : IDADE_ESCONDIDO_MS)) await atualizar($)
383
+ await registrarMostrados($)
384
+ }
385
+
386
+ /** A linha da faixa pela ponte, so quando o texto muda; sem entrega que espere a nota, a linha sai. */
387
+ async function publicarFaixa($: EngineInterface): Promise<void> {
388
+ const texto = textoDaFaixa(quadro)
389
+ if (texto === ultimaFaixa) return
390
+ try {
391
+ if (texto === undefined) await $.ork.faixa({ mod: MOD })
392
+ else await $.ork.faixa({ mod: MOD, texto })
393
+ ultimaFaixa = texto
394
+ } catch {
395
+ // A ponte recusou ou falhou: a proxima leitura tenta de novo.
396
+ }
397
+ }
398
+
399
+ /** O desenho anota o que pos na janela; quem registra e o tique ou a leitura, fora do desenho. */
400
+ function anotarVistos(vistos: readonly RitualMostrado[]): void {
401
+ for (const m of vistos) vistosNoDesenho.set(`${m.chave}|${m.texto}`, m)
402
+ }
403
+
404
+ /**
405
+ * O que o desenho pos na janela de rolagem do painel posto e a vista vai ao registro da ponte (M5 do dono), uma vez por
406
+ * chave e texto enquanto o texto existir no painel: o Telegram nao repete o que o dono ja viu no terminal.
407
+ */
408
+ async function registrarMostrados($: EngineInterface): Promise<void> {
409
+ const existentes = new Set(linhasDoPainel(quadro, { agora: 0, foco, prova, passo, notas, reservas })
410
+ .flatMap(l => (l.mostrado ? [`${l.mostrado.chave}|${l.mostrado.texto}`] : [])))
411
+ for (const vista of [...jaMostrado]) if (!existentes.has(vista)) jaMostrado.delete(vista)
412
+ for (const vista of [...vistosNoDesenho.keys()]) if (!existentes.has(vista)) vistosNoDesenho.delete(vista)
413
+ if (vistosNoDesenho.size === 0) return
414
+ // Com o painel escondido, o que foi anotado e descartado: o proximo desenho a vista anota de novo.
415
+ if (!await painelAVista($)) { vistosNoDesenho.clear(); return }
416
+ for (const [vista, m] of [...vistosNoDesenho]) {
417
+ vistosNoDesenho.delete(vista)
418
+ if (jaMostrado.has(vista)) continue
419
+ try {
420
+ await $.ork.mostrado({ mod: MOD, canal: 'painel', chave: m.chave, thread: m.thread, texto: m.texto })
421
+ jaMostrado.add(vista)
422
+ } catch {
423
+ // A ponte recusou ou falhou: volta para a proxima vez.
424
+ vistosNoDesenho.set(vista, m)
425
+ }
426
+ }
427
+ }
428
+
429
+ export const register: Register = on => {
430
+ on('session.start', async ($, e, next) => {
431
+ if (e.isInteractive) await conferirPapel($, 0)
432
+ return next(e)
433
+ })
434
+
435
+ // `/nota [thread]` abre o painel em qualquer largura e rele as entregas agora. Com uma thread (a Torre abre o Ritual
436
+ // assim), o painel abre no replay da prova dela. O comando so existe com o Ritual ligado; a conferencia do papel
437
+ // aqui so cobre o caso raro de o comando ter sobrado de uma carga anterior.
438
+ on('command.run', { command: 'nota' }, async ($, e) => {
439
+ if (!ativa && !desistiu) await conferirPapel($, ESPERAS_DO_PAPEL_MS.length)
440
+ if (!ativa) return { text: 'O Ritual do MASTER só abre na sessão do dono de um projeto do Orkastery.' }
441
+ const pedida = typeof e.args === 'string' ? e.args.trim() : ''
442
+ let thread: string | null = null
443
+ let aviso = ''
444
+ if (pedida) {
445
+ try {
446
+ thread = exigirThread(pedida)
447
+ } catch {
448
+ aviso = ` O id ${umaLinha(pedida, 80)} não é de thread; o painel abre na lista das entregas.`
449
+ }
450
+ }
451
+ if (thread) await focar($, thread)
452
+ else await soltarFoco($)
453
+ const aberto = await $.ui.open({ id: PAINEL, title: TITULO })
454
+ emSegundoPlano($, atualizar($, true), 'a leitura das entregas')
455
+ const onde = thread ? ` na prova da ${thread}` : ''
456
+ return { text: (aberto.isPlaced ? `Ritual do MASTER aberto${onde}.` : `Ritual do MASTER aberto${onde}, à espera de lugar: ${aberto.reason}`) + aviso }
457
+ })
458
+
459
+ // O ledger de uma thread mudou (entrega, MASTER, nota): a releitura entra no proximo lote. Os caminhos vigiados sao
460
+ // os que a ponte pede para o papel dono; o Ritual nao pede outros.
461
+ on('classic.FileChanged', async ($, e, next) => {
462
+ if (ativa && threadDoLedger(e.file_path) !== null) agendar($)
463
+ return next(e)
464
+ }).catch(($, e, next) => next(e))
465
+
466
+ // O painel: a lista das entregas ou o replay da prova. O desenho nunca escreve: so anota o que pos na janela de
467
+ // rolagem, para o registro do mostrado.
468
+ on('ui.render', { component: 'Pane', requestId: PAINEL }, async ($, e) => {
469
+ const { Box, Text, Button } = $.ui.resolve(e)
470
+ const q = (await $.state.get(QUADRO)).value ?? QUADRO_VAZIO
471
+ const emFoco = (await $.state.get(FOCO)).value ?? null
472
+ const p = (await $.state.get(PASSO)).value ?? 0
473
+ const pr = (await $.state.get(PROVA)).value ?? null
474
+ const ns = (await $.state.get(NOTAS)).value ?? {}
475
+ const rs = (await $.state.get(RESERVAS)).value ?? {}
476
+ const agora = await $.clock.now()
477
+ const comProblema: RitualQuadro = problemaSemAviso ? { ...q, avisos: [...q.avisos, problemaSemAviso] } : q
478
+ const linhas = linhasDoPainel(comProblema, { agora, foco: emFoco, prova: pr, passo: p, notas: ns, reservas: rs })
479
+ anotarVistos(mostradosVisiveis(linhas, e.props.scroll, e.props.bodyColumns))
480
+ return desenharRitual({ Box, Text, Button }, linhas, {
481
+ foco: emFoco,
482
+ atualizar: () => {
483
+ emSegundoPlano($, atualizar($, true), 'a releitura das entregas')
484
+ if (emFoco) emSegundoPlano($, lerProva($, emFoco), 'a releitura das claims')
485
+ },
486
+ verProva: thread => { emSegundoPlano($, focar($, thread), 'o Ver a prova') },
487
+ voltar: () => { emSegundoPlano($, soltarFoco($), 'o Voltar') },
488
+ anterior: () => { emSegundoPlano($, andar($, -1), 'o Anterior') },
489
+ proxima: () => { emSegundoPlano($, andar($, 1), 'o Próxima') },
490
+ darNota: thread => { emSegundoPlano($, darNota($, thread), 'o Dar a nota') },
491
+ telegram: thread => { emSegundoPlano($, pedirTelegram($, thread), 'o pedido pelo Telegram') },
492
+ })
493
+ })
494
+ }