@orkastery/cli 0.5.3 → 0.5.6

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 (212) hide show
  1. package/adapters/claude-code/README.md +158 -0
  2. package/adapters/claude-code/mods/autoridade.json +56 -0
  3. package/adapters/claude-code/mods/ork-clima/.claude-plugin/plugin.json +14 -0
  4. package/adapters/claude-code/mods/ork-clima/hooks/contas.ts +283 -0
  5. package/adapters/claude-code/mods/ork-clima/hooks/hooks.json +3 -0
  6. package/adapters/claude-code/mods/ork-clima/hooks/previsao.ts +393 -0
  7. package/adapters/claude-code/mods/ork-clima/hooks/register.ts +205 -0
  8. package/adapters/claude-code/mods/ork-clima/orkastery-mod.json +5 -0
  9. package/adapters/claude-code/mods/ork-clima/types/index.d.ts +80 -0
  10. package/adapters/claude-code/mods/ork-freio/.claude-plugin/plugin.json +14 -0
  11. package/adapters/claude-code/mods/ork-freio/hooks/carga.ts +160 -0
  12. package/adapters/claude-code/mods/ork-freio/hooks/conferencia.ts +309 -0
  13. package/adapters/claude-code/mods/ork-freio/hooks/hooks.json +3 -0
  14. package/adapters/claude-code/mods/ork-freio/hooks/lote.ts +270 -0
  15. package/adapters/claude-code/mods/ork-freio/hooks/register.ts +285 -0
  16. package/adapters/claude-code/mods/ork-freio/orkastery-mod.json +5 -0
  17. package/adapters/claude-code/mods/ork-freio/types/index.d.ts +127 -0
  18. package/adapters/claude-code/mods/ork-mesa/.claude-plugin/plugin.json +14 -0
  19. package/adapters/claude-code/mods/ork-mesa/hooks/cartoes.tsx +246 -0
  20. package/adapters/claude-code/mods/ork-mesa/hooks/decidir.ts +110 -0
  21. package/adapters/claude-code/mods/ork-mesa/hooks/fila.ts +253 -0
  22. package/adapters/claude-code/mods/ork-mesa/hooks/hooks.json +3 -0
  23. package/adapters/claude-code/mods/ork-mesa/hooks/obvia.ts +98 -0
  24. package/adapters/claude-code/mods/ork-mesa/hooks/register.tsx +493 -0
  25. package/adapters/claude-code/mods/ork-mesa/orkastery-mod.json +5 -0
  26. package/adapters/claude-code/mods/ork-mesa/types/index.d.ts +131 -0
  27. package/adapters/claude-code/mods/ork-pista/.claude-plugin/plugin.json +14 -0
  28. package/adapters/claude-code/mods/ork-pista/hooks/acoes.ts +71 -0
  29. package/adapters/claude-code/mods/ork-pista/hooks/ci.ts +117 -0
  30. package/adapters/claude-code/mods/ork-pista/hooks/hooks.json +3 -0
  31. package/adapters/claude-code/mods/ork-pista/hooks/pista.tsx +356 -0
  32. package/adapters/claude-code/mods/ork-pista/hooks/register.tsx +439 -0
  33. package/adapters/claude-code/mods/ork-pista/orkastery-mod.json +5 -0
  34. package/adapters/claude-code/mods/ork-pista/types/index.d.ts +85 -0
  35. package/adapters/claude-code/mods/ork-ponte/.claude-plugin/plugin.json +13 -0
  36. package/adapters/claude-code/mods/ork-ponte/hooks/faixa.tsx +57 -0
  37. package/adapters/claude-code/mods/ork-ponte/hooks/guarda.ts +410 -0
  38. package/adapters/claude-code/mods/ork-ponte/hooks/hooks.json +3 -0
  39. package/adapters/claude-code/mods/ork-ponte/hooks/mostrado.ts +55 -0
  40. package/adapters/claude-code/mods/ork-ponte/hooks/nucleo.ts +157 -0
  41. package/adapters/claude-code/mods/ork-ponte/hooks/papel.ts +42 -0
  42. package/adapters/claude-code/mods/ork-ponte/hooks/register.ts +331 -0
  43. package/adapters/claude-code/mods/ork-ponte/hooks/vigia.ts +76 -0
  44. package/adapters/claude-code/mods/ork-ponte/orkastery-mod.json +5 -0
  45. package/adapters/claude-code/mods/ork-ponte/types/index.d.ts +178 -0
  46. package/adapters/claude-code/mods/ork-portao/.claude-plugin/plugin.json +14 -0
  47. package/adapters/claude-code/mods/ork-portao/hooks/hooks.json +3 -0
  48. package/adapters/claude-code/mods/ork-portao/hooks/lease.ts +201 -0
  49. package/adapters/claude-code/mods/ork-portao/hooks/plano.ts +304 -0
  50. package/adapters/claude-code/mods/ork-portao/hooks/register.ts +339 -0
  51. package/adapters/claude-code/mods/ork-portao/hooks/verify.ts +152 -0
  52. package/adapters/claude-code/mods/ork-portao/orkastery-mod.json +5 -0
  53. package/adapters/claude-code/mods/ork-portao/types/index.d.ts +60 -0
  54. package/adapters/claude-code/mods/ork-radar/.claude-plugin/plugin.json +14 -0
  55. package/adapters/claude-code/mods/ork-radar/hooks/colisao.ts +420 -0
  56. package/adapters/claude-code/mods/ork-radar/hooks/hooks.json +3 -0
  57. package/adapters/claude-code/mods/ork-radar/hooks/mapa.tsx +322 -0
  58. package/adapters/claude-code/mods/ork-radar/hooks/register.tsx +470 -0
  59. package/adapters/claude-code/mods/ork-radar/orkastery-mod.json +5 -0
  60. package/adapters/claude-code/mods/ork-radar/types/index.d.ts +72 -0
  61. package/adapters/claude-code/mods/ork-sentinela/.claude-plugin/plugin.json +14 -0
  62. package/adapters/claude-code/mods/ork-sentinela/hooks/alertas.ts +85 -0
  63. package/adapters/claude-code/mods/ork-sentinela/hooks/despacho.ts +156 -0
  64. package/adapters/claude-code/mods/ork-sentinela/hooks/hooks.json +3 -0
  65. package/adapters/claude-code/mods/ork-sentinela/hooks/register.ts +511 -0
  66. package/adapters/claude-code/mods/ork-sentinela/hooks/vigia.ts +186 -0
  67. package/adapters/claude-code/mods/ork-sentinela/orkastery-mod.json +5 -0
  68. package/adapters/claude-code/mods/ork-sentinela/types/index.d.ts +65 -0
  69. package/adapters/claude-code/mods/ork-torre/.claude-plugin/plugin.json +14 -0
  70. package/adapters/claude-code/mods/ork-torre/hooks/agenda.ts +60 -0
  71. package/adapters/claude-code/mods/ork-torre/hooks/detalhe.tsx +236 -0
  72. package/adapters/claude-code/mods/ork-torre/hooks/frases.ts +42 -0
  73. package/adapters/claude-code/mods/ork-torre/hooks/hooks.json +3 -0
  74. package/adapters/claude-code/mods/ork-torre/hooks/painel.tsx +344 -0
  75. package/adapters/claude-code/mods/ork-torre/hooks/rede.ts +97 -0
  76. package/adapters/claude-code/mods/ork-torre/hooks/register.tsx +437 -0
  77. package/adapters/claude-code/mods/ork-torre/orkastery-mod.json +5 -0
  78. package/adapters/claude-code/mods/ork-torre/types/index.d.ts +108 -0
  79. package/adapters/hermes/skills/orkastery-devmaster/SKILL.md +1 -1
  80. package/adapters/openclaw/README.md +2 -1
  81. package/adapters/openclaw/openclaw.plugin.json +1 -1
  82. package/adapters/openclaw/package.json +1 -1
  83. package/assets/docs-en/markdownlint-cli2.jsonc +22 -0
  84. package/assets/docs-en/padroes/documentacao-de-produto.md +135 -0
  85. package/assets/docs-en/padroes/roadmap-de-produto.md +100 -0
  86. package/assets/docs-en/produto/README.md +14 -0
  87. package/assets/docs-en/produto/_modelo-feature.md +62 -0
  88. package/assets/docs-en/roadmap/README.md +14 -0
  89. package/assets/docs-en/roadmap/_modelo-item.md +75 -0
  90. package/dist/adapters/claude-bg.js +77 -13
  91. package/dist/adapters/codex.js +10 -1
  92. package/dist/agents-md.js +24 -15
  93. package/dist/auditrun.js +17 -2
  94. package/dist/board.js +4 -3
  95. package/dist/branch-de-estado.js +23 -1
  96. package/dist/catalogo.js +33 -0
  97. package/dist/ci.js +13 -7
  98. package/dist/claims-conferir.js +342 -0
  99. package/dist/claims.js +3 -1
  100. package/dist/company-brain-client.js +36 -1
  101. package/dist/company-brain-journal.js +2 -1
  102. package/dist/company-brain-migration.js +36 -1
  103. package/dist/conducao-sentinela.js +388 -0
  104. package/dist/conducao-texto.js +12 -18
  105. package/dist/conducao.js +122 -30
  106. package/dist/conhecimento.js +490 -0
  107. package/dist/docs.js +22 -8
  108. package/dist/doctor-pulse-cron.js +182 -27
  109. package/dist/doctor-rede.js +18 -17
  110. package/dist/doctor.js +115 -111
  111. package/dist/entrega-pr.js +22 -21
  112. package/dist/escopo-escrita.js +10 -1
  113. package/dist/estado-thread.js +23 -1
  114. package/dist/experiencia-instalacao.js +45 -25
  115. package/dist/fabrica-batida.js +310 -0
  116. package/dist/fabrica-estado.js +38 -8
  117. package/dist/fabrica-publicar.js +3 -1
  118. package/dist/fila-pesada.js +444 -0
  119. package/dist/fix.js +3 -2
  120. package/dist/forja.js +1 -1
  121. package/dist/gates.js +3 -28
  122. package/dist/handoff.js +10 -6
  123. package/dist/hitl-local-receipt.js +2 -0
  124. package/dist/hitl-resumo.js +29 -3
  125. package/dist/hitl-tempo-parado.js +4 -4
  126. package/dist/hitl.js +29 -33
  127. package/dist/horario.js +14 -7
  128. package/dist/hosts.js +52 -1
  129. package/dist/impedimento.js +108 -14
  130. package/dist/index.js +623 -521
  131. package/dist/indice.js +7 -1
  132. package/dist/init.js +43 -47
  133. package/dist/instabilidade.js +144 -0
  134. package/dist/integracoes-locais.js +2 -0
  135. package/dist/intelligence-graph-cli.js +92 -21
  136. package/dist/intelligence-graph-contexto.js +56 -17
  137. package/dist/intelligence-graph-contract.js +2 -1
  138. package/dist/intelligence-graph-extract-md.js +56 -4
  139. package/dist/intelligence-graph-extract-ts.js +71 -3
  140. package/dist/intelligence-graph-query.js +66 -19
  141. package/dist/ledger.js +15 -1
  142. package/dist/liveness.js +6 -3
  143. package/dist/locale-ajuda.js +761 -0
  144. package/dist/locale-gerados.js +160 -0
  145. package/dist/locale-master.js +99 -0
  146. package/dist/locale-onboarding.js +92 -0
  147. package/dist/locale-phase.js +151 -0
  148. package/dist/locale-pulse.js +211 -0
  149. package/dist/locale-retry.js +385 -0
  150. package/dist/locale-roadmap.js +102 -0
  151. package/dist/locale-ship.js +494 -0
  152. package/dist/locale-thread.js +277 -0
  153. package/dist/locale-verify.js +125 -0
  154. package/dist/locale.js +714 -0
  155. package/dist/manifest.js +129 -3
  156. package/dist/maquina.js +23 -4
  157. package/dist/master-digest.js +6 -1
  158. package/dist/master-nota.js +4 -6
  159. package/dist/master.js +21 -24
  160. package/dist/mcp-artifacts.js +11 -2
  161. package/dist/mcp-git.js +23 -6
  162. package/dist/mcp-grafo.js +7 -4
  163. package/dist/mcp-server.js +8 -2
  164. package/dist/mcp-ship.js +3 -1
  165. package/dist/mcp-verify.js +6 -1
  166. package/dist/modos.js +3 -6
  167. package/dist/network-roadmap.js +5 -2
  168. package/dist/noctum-arco.js +911 -0
  169. package/dist/noctum-capacidade.js +141 -0
  170. package/dist/noctum-envelope.js +810 -0
  171. package/dist/noctum-execucao.js +304 -0
  172. package/dist/noctum-fila.js +280 -0
  173. package/dist/ocupacao.js +29 -2
  174. package/dist/onboarding.js +40 -21
  175. package/dist/orkmind.js +9 -2
  176. package/dist/orquestracao.js +13 -12
  177. package/dist/parado-no-condutor.js +34 -9
  178. package/dist/phase.js +94 -39
  179. package/dist/policies.js +4 -4
  180. package/dist/portfolio.js +4 -2
  181. package/dist/postura-local.js +234 -0
  182. package/dist/preflight.js +11 -10
  183. package/dist/procedencia.js +225 -0
  184. package/dist/projeto-alvo.js +127 -4
  185. package/dist/prompts.js +6 -2
  186. package/dist/pulse-delivery.js +13 -2
  187. package/dist/pulse.js +52 -19
  188. package/dist/ratelimit.js +4 -2
  189. package/dist/redacao-saida.js +63 -0
  190. package/dist/rede-status.js +3 -2
  191. package/dist/retry.js +232 -148
  192. package/dist/roadmap-reservas.js +20 -3
  193. package/dist/roadmap-status.js +129 -33
  194. package/dist/runtime-context.js +181 -3
  195. package/dist/runtimes.js +2 -1
  196. package/dist/session-events.js +7 -3
  197. package/dist/session-watcher-claude.js +7 -1
  198. package/dist/session-watcher.js +185 -33
  199. package/dist/sessoes-contas.js +1 -1
  200. package/dist/ship-publicar.js +800 -0
  201. package/dist/ship.js +84 -81
  202. package/dist/steal.js +77 -0
  203. package/dist/thread-close.js +3 -2
  204. package/dist/thread.js +153 -82
  205. package/dist/util.js +8 -1
  206. package/dist/verify.js +141 -52
  207. package/dist/vida-da-thread.js +254 -0
  208. package/dist/worktree-local.js +165 -0
  209. package/dist/worktree.js +20 -0
  210. package/dist/yaml.js +27 -8
  211. package/package.json +1 -1
  212. package/schemas/code-artifact-graph.v1.schema.json +2 -1
@@ -0,0 +1,127 @@
1
+ // Contrato de tipos do Freio de Lote (RM-059, fatia F2). O Freio nao acrescenta noun ao `$`: este arquivo
2
+ // declara o estado dele (`PluginState['ork-freio']`) e os tipos que os modulos puros e os testes usam.
3
+ // Sem import nem referencia, como o motor exige de um contrato.
4
+
5
+ /** Uma claim ativa da thread, como o Freio a usa: o id, o arquivo e os comandos de verificacao. */
6
+ export type FreioClaim = {
7
+ id: string
8
+ arquivo: string | null
9
+ comandos: string[]
10
+ }
11
+
12
+ /** O que a carga leu do nucleo (T2.1). */
13
+ export type FreioCarga = {
14
+ /** A thread do despacho, do contexto da ponte. */
15
+ thread: string
16
+ /** O status da thread no retrato do nucleo; `fechada` deixa o Freio dormente. */
17
+ statusDaThread: string | null
18
+ /** As claims nao retiradas; `null` enquanto o nucleo nao respondeu. */
19
+ claims: FreioClaim[] | null
20
+ /** Quando as claims foram lidas, em ms desde a epoca. */
21
+ claimsEm: number
22
+ /** O maior numero de claim da thread na leitura, retiradas incluidas (falta na carga gravada antes dele). */
23
+ maiorClaim?: number
24
+ /** A uniao dos `touch_paths` das tarefas do plano; `null` sem plano ou sem nenhum. */
25
+ touchPaths: string[] | null
26
+ /** O plano ja foi lido (com ou sem `touch_paths`). */
27
+ planoLido: boolean
28
+ /** Quando o plano foi lido, em ms desde a epoca. */
29
+ planoEm: number
30
+ /** O que falhou na ultima carga, para a linha de status e o teste. */
31
+ erros: string[]
32
+ }
33
+
34
+ /** A foto do `git status` da worktree: o HEAD e a assinatura de cada caminho sujo. */
35
+ export type FreioFoto = {
36
+ head: string | null
37
+ /** Caminho relativo a raiz da sessao para `XY|tamanho|mtimeMs` (ou `XY|ausente`). */
38
+ caminhos: Record<string, string>
39
+ }
40
+
41
+ /** Uma claim afetada pelo lote, na fila da conferencia (T2.2). */
42
+ export type FreioPedido = {
43
+ id: string
44
+ /** 3 pelo `arquivo`, 2 pelo caminho citado no comando, 1 pelo nome-base citado (D5). */
45
+ forca: number
46
+ /** Os caminhos tocados que a afetaram. */
47
+ caminhos: string[]
48
+ }
49
+
50
+ /** O que o Freio vigia entre os lotes. */
51
+ export type FreioVigia = {
52
+ thread: string
53
+ /** A ultima foto do `git status`; `null` antes da primeira. */
54
+ foto: FreioFoto | null
55
+ /** O numero do lote, que marca quando cada caminho foi tocado. */
56
+ seq: number
57
+ /** O ultimo lote que tocou cada caminho. */
58
+ toques: Record<string, number>
59
+ /** As claims afetadas que esperam conferencia. */
60
+ fila: FreioPedido[]
61
+ /** Os caminhos fora dos `touch_paths` ja avisados nesta sessao. */
62
+ avisadosFora: string[]
63
+ // Os campos da conferencia (T2.3) faltam na vigia gravada antes deles; `comConferencia` os preenche.
64
+ /** A rodada em curso no nucleo, ou `null`. */
65
+ rodada?: FreioRodada | null
66
+ /** O que o lote seguinte devolve ao modelo. */
67
+ entrega?: FreioEntrega
68
+ /** As claims cuja ultima conferencia reprovou. */
69
+ emRisco?: string[]
70
+ /** Ate quando o Freio nao pede conferencia (depois de uma indisponivel), em ms desde a epoca. */
71
+ pausaAte?: number
72
+ /** Por que a ultima conferencia ficou indisponivel, ou `null`. */
73
+ indisponivel?: string | null
74
+ /** O modelo ja foi avisado da conferencia indisponivel nesta sessao. */
75
+ avisouIndisponivel?: boolean
76
+ /** Caminhos fora dos `touch_paths` que o laco principal ainda nao recebeu (lote de subagente nao recebe aviso). */
77
+ foraPendentes?: string[]
78
+ /** As claims cuja falha o modelo ja recebeu: so elas voltam como recuperadas quando passam. */
79
+ avisadas?: string[]
80
+ }
81
+
82
+ /** Uma rodada de conferencia: as claims, os caminhos que afetaram cada uma e o lote em que ela comecou (T2.3). */
83
+ export type FreioRodada = {
84
+ ids: string[]
85
+ caminhos: Record<string, string[]>
86
+ /** A forca de cada claim, para ela voltar a fila no mesmo lugar (recusa por claim, conducao alheia). */
87
+ forcas?: Record<string, number>
88
+ /** O numero do lote no inicio; toque mais novo deixa o resultado da claim velho (D6). */
89
+ inicio: number
90
+ }
91
+
92
+ /** Uma claim que a conferencia reprovou (`claims.failed`), como volta ao modelo. */
93
+ export type FreioFalha = {
94
+ id: string
95
+ alegacao: string
96
+ arquivo: string | null
97
+ comando: string
98
+ codigo: number | null
99
+ /** O fim da saida, ja redigido pelo nucleo. */
100
+ fim: string
101
+ }
102
+
103
+ /** Uma claim cuja conferencia terminou sem veredito (prazo, comando que nao rodou). */
104
+ export type FreioSemVeredito = {
105
+ id: string
106
+ motivo: string
107
+ }
108
+
109
+ /** O que o lote seguinte devolve ao modelo. */
110
+ export type FreioEntrega = {
111
+ falhas: FreioFalha[]
112
+ semVeredito: FreioSemVeredito[]
113
+ recuperadas: string[]
114
+ /** O motivo da conferencia indisponivel, so na primeira vez da sessao. */
115
+ indisponivel: string | null
116
+ }
117
+
118
+ declare module 'claude-code' {
119
+ interface PluginState {
120
+ 'ork-freio': {
121
+ /** O que a carga leu do nucleo. */
122
+ carga: FreioCarga
123
+ /** O que o Freio vigia entre os lotes. */
124
+ vigia: FreioVigia
125
+ }
126
+ }
127
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "ork-mesa",
3
+ "version": "0.1.0",
4
+ "description": "Mesa do Dono do Orkastery no Claude Code: tudo que espera o dono num painel, um cartão por pedido com a recomendação na frente e a decisão pelo diálogo nativo do host. Só decide pelo dono pela decisão autônoma do núcleo, quando a decisão é óbvia, e nunca responde um pedido.",
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,246 @@
1
+ // Os cartoes da Mesa do Dono (RM-059 F5; D4 da thread ork-rm059mesa), em funcoes puras: a fila vira linhas da tela,
2
+ // uma por elemento, e as linhas viram a arvore do painel com os elementos que a superficie resolveu. Cada pergunta
3
+ // traz a recomendada primeiro, com o porque, depois as outras alternativas com a consequencia; a decisao que a
4
+ // fabrica (ou a Mesa) tomou pelo dono traz o que, o porque, como mudar e o custo de reverter. O unico botao com
5
+ // efeito e o Decidir, que so pede ao nucleo o dialogo nativo do pedido. So importa tipos e modulos puros da Mesa.
6
+ import type { MesaDecidindo, MesaDecisao, MesaFila, MesaObvia, MesaOutro, MesaPergunta } from '../types'
7
+ import { duracao, esperamODono, MAXIMO_DE_DECISOES, MAXIMO_DE_OUTROS, TAMANHO_DO_LOTE } from './fila'
8
+ import { FALHA_DA_CONDICAO, testeDaObvia } from './obvia'
9
+
10
+ export type MesaMostrado = { chave: string; thread: string | null; texto: string }
11
+
12
+ export type LinhaDaMesa = {
13
+ tipo: 'titulo' | 'aviso' | 'foco' | 'secao' | 'cabecalho' | 'texto' | 'dim' | 'decidir' | 'nav'
14
+ chave: string
15
+ texto: string
16
+ /** O pedido do botao Decidir. */
17
+ pedidoId?: string
18
+ /** O que vai ao registro do mostrado da ponte quando a linha esta na janela de rolagem do painel a vista. */
19
+ mostrado?: MesaMostrado
20
+ }
21
+
22
+ export type EstadoDaMesa = {
23
+ fila: MesaFila
24
+ foco: string | null
25
+ pagina: number
26
+ decidindo: Readonly<Record<string, MesaDecidindo>>
27
+ obvias: Readonly<Record<string, MesaObvia>>
28
+ }
29
+
30
+ function plural(n: number, um: string, varios: string): string {
31
+ return `${n} ${n === 1 ? um : varios}`
32
+ }
33
+
34
+ /** O prazo como o dono le: o horario local que o nucleo mandou, ou quanto falta. */
35
+ function textoDoPrazo(p: MesaPergunta, agora: number): string | null {
36
+ if (p.prazoLocal) return `prazo ${p.prazoLocal}`
37
+ const t = Date.parse(p.prazo ?? '')
38
+ if (!Number.isFinite(t)) return null
39
+ return t > agora ? `vence em ${duracao(t - agora)}` : `venceu há ${duracao(agora - t)}`
40
+ }
41
+
42
+ /** As paginas do lote de perguntas, e a pagina pedida dentro delas. */
43
+ export function paginaDoLote(total: number, pagina: number): { pagina: number; paginas: number } {
44
+ const paginas = Math.max(1, Math.ceil(total / TAMANHO_DO_LOTE))
45
+ return { pagina: Math.min(Math.max(0, Math.floor(pagina) || 0), paginas - 1), paginas }
46
+ }
47
+
48
+ /** O cartao de uma pergunta. */
49
+ export function linhasDaPergunta(p: MesaPergunta, e: EstadoDaMesa, agora: number): LinhaDaMesa[] {
50
+ const k = (s: string) => `${p.pedidoId}:${s}`
51
+ const obvia = e.obvias[p.pedidoId]
52
+ const decidida = obvia?.estado === 'registrada'
53
+ const decidindo = e.decidindo[p.pedidoId]
54
+ const cabecalho = [p.thread, p.fase, p.codigo ? `código ${p.codigo}` : null, textoDoPrazo(p, agora)].filter(Boolean).join(' · ')
55
+ const linhas: LinhaDaMesa[] = [{
56
+ tipo: 'cabecalho', chave: k('cabecalho'), texto: `${decidida ? '✓' : '?'} ${cabecalho}`,
57
+ mostrado: { chave: `hitl:${p.pedidoId}`, thread: p.thread, texto: `${p.thread}: espera você: ${p.pergunta}` },
58
+ }]
59
+ linhas.push({ tipo: 'texto', chave: k('pergunta'), texto: ` ${p.pergunta}` })
60
+ if (decidida) {
61
+ linhas.push({ tipo: 'texto', chave: k('obvia'), texto: ` decidido sem te chamar: segue a recomendada no prazo; ${comoMudarDaObvia(p, agora)}` })
62
+ } else if (obvia?.estado === 'incerta') {
63
+ linhas.push({ tipo: 'dim', chave: k('obvia'), texto: ' a Mesa pediu ao núcleo a decisão óbvia e confere na próxima leitura se ela foi gravada' })
64
+ } else if (obvia?.estado === 'recusada') {
65
+ linhas.push({ tipo: 'aviso', chave: k('obvia'), texto: ` a Mesa não decidiu: ${obvia.texto}` })
66
+ } else if (!p.conferida) {
67
+ linhas.push({ tipo: 'dim', chave: k('porque-pergunta'), texto: ' não conferido no núcleo agora: sem Decidir e sem decisão óbvia; use a reserva' })
68
+ } else {
69
+ // Sem criterio escrito e o caso de todo gate que espera o dono: dizer isso em cada cartao so repetiria a regra.
70
+ const teste = testeDaObvia(p, agora)
71
+ const motivo = teste.obvia ? undefined : teste.falhou[0]
72
+ if (motivo && motivo !== 'criterio') linhas.push({ tipo: 'dim', chave: k('porque-pergunta'), texto: ` pergunta: ${FALHA_DA_CONDICAO[motivo]}` })
73
+ }
74
+ const recomendada = p.alternativas.find(a => a.recomendada)
75
+ if (recomendada) {
76
+ linhas.push({ tipo: 'texto', chave: k('recomendada'), texto: ` Recomendada: ${recomendada.chave}) ${recomendada.texto}` })
77
+ if (recomendada.porque) linhas.push({ tipo: 'dim', chave: k('porque'), texto: ` porque: ${recomendada.porque}` })
78
+ if (recomendada.consequencia) linhas.push({ tipo: 'dim', chave: k('consequencia'), texto: ` se escolher: ${recomendada.consequencia}` })
79
+ } else if (p.recomendacao) {
80
+ linhas.push({ tipo: 'texto', chave: k('recomendada'), texto: ` Recomendação: ${p.recomendacao}` })
81
+ }
82
+ for (const a of p.alternativas) {
83
+ if (a.recomendada) continue
84
+ linhas.push({ tipo: 'dim', chave: k(`alt:${a.chave}`), texto: ` ${a.chave}) ${a.texto}${a.consequencia ? `: ${a.consequencia}` : ''}` })
85
+ }
86
+ if (p.irreversivel) linhas.push({ tipo: 'aviso', chave: k('sem-volta'), texto: ` sem volta: ${p.ato ?? 'ato irreversível'}` })
87
+ if (decidindo?.estado === 'abrindo') {
88
+ linhas.push({ tipo: 'texto', chave: k('decidindo'), texto: ' diálogo pedido: responda na janela do Claude Code' })
89
+ } else {
90
+ if (decidindo) linhas.push({ tipo: decidindo.estado === 'erro' ? 'aviso' : 'dim', chave: k('decidindo'), texto: ` ${decidindo.texto}` })
91
+ if (p.canalClaudeCode === 'disponivel') linhas.push({ tipo: 'decidir', chave: `decidir:${p.pedidoId}`, texto: 'Decidir', pedidoId: p.pedidoId })
92
+ }
93
+ if (p.reserva) {
94
+ const telegram = p.contrato === 'ork.hitl/v2' && p.alvo === 'gate' && p.codigo !== null
95
+ linhas.push({ tipo: 'dim', chave: k('reserva'), texto: telegram ? ` reserva: ${p.reserva} pelo Telegram` : ` reserva: ${p.reserva}` })
96
+ }
97
+ return linhas
98
+ }
99
+
100
+ /** Como o dono muda a decisao obvia que a Mesa registrou: o Decidir ou a reserva antes do prazo; depois dele, nada. */
101
+ function comoMudarDaObvia(p: MesaPergunta, agora: number): string {
102
+ const prazo = Date.parse(p.prazo ?? '')
103
+ if (Number.isFinite(prazo) && prazo <= agora) return 'o prazo passou e a recomendada seguiu'
104
+ return p.canalClaudeCode === 'disponivel' ? 'para mudar, Decidir antes do prazo' : 'para mudar, use a reserva antes do prazo'
105
+ }
106
+
107
+ /** O cartao de uma decisao que a fabrica (ou a Mesa) tomou sem chamar o dono. */
108
+ export function linhasDaDecisao(d: MesaDecisao, agora: number): LinhaDaMesa[] {
109
+ const k = (s: string) => `${d.id}:${s}`
110
+ const em = Date.parse(d.em)
111
+ const quando = Number.isFinite(em) ? ` · há ${duracao(agora - em)}` : ''
112
+ const marca = d.revertidaPor ? ' · revertida' : d.nova ? ' · nova' : ''
113
+ return [
114
+ { tipo: 'cabecalho', chave: k('cabecalho'), texto: `${d.revertidaPor ? '↺' : '✓'} ${d.thread} · ${d.fase}${quando}${marca}`,
115
+ mostrado: { chave: `decisao:${d.id}`, thread: d.thread || null, texto: `${d.thread}: decidido sem te chamar: ${d.decidido}` } },
116
+ { tipo: 'texto', chave: k('decidido'), texto: ` ${d.decidido}` },
117
+ ...(d.revertidaPor ? [{ tipo: 'aviso' as const, chave: k('revertida'), texto: ` desfeita pela decisão ${d.revertidaPor.slice(0, 8)}` }] : []),
118
+ ...(d.reverte ? [{ tipo: 'dim' as const, chave: k('reverte'), texto: ` desfaz a decisão ${d.reverte.slice(0, 8)}` }] : []),
119
+ { tipo: 'dim', chave: k('porque'), texto: ` porque: ${d.porque}` },
120
+ { tipo: 'dim', chave: k('como-mudar'), texto: ` como mudar: ${d.comoMudar}` },
121
+ { tipo: 'dim', chave: k('custo'), texto: ` reverter: agora, ${d.custoAgora}; depois, ${d.custoDepois}` },
122
+ { tipo: 'dim', chave: k('quem'), texto: ` quem decidiu: ${d.quemDecidiu}` },
123
+ ]
124
+ }
125
+
126
+ /** O cartao de uma parada sem pedido ou de um impedimento: so leitura, com a recomendacao e o comando. */
127
+ export function linhasDoOutro(o: MesaOutro, agora: number): LinhaDaMesa[] {
128
+ const k = (s: string) => `${o.chave}:${s}`
129
+ const desde = Date.parse(o.desdeEm ?? '')
130
+ const partes = [o.thread ?? 'sem thread', o.fase, o.motivo, Number.isFinite(desde) ? `há ${duracao(agora - desde)}` : null,
131
+ o.impedimento ? 'só você resolve' : null].filter(Boolean)
132
+ const linhas: LinhaDaMesa[] = [{
133
+ tipo: 'cabecalho', chave: k('cabecalho'), texto: `! ${partes.join(' · ')}`,
134
+ mostrado: { chave: `parada:${o.chave}`, thread: o.thread, texto: `${o.thread ?? 'sem thread'}: ${o.motivo}: ${o.texto}` },
135
+ }]
136
+ if (o.texto) linhas.push({ tipo: 'texto', chave: k('texto'), texto: ` ${o.texto}` })
137
+ if (o.recomendacao) linhas.push({ tipo: 'dim', chave: k('recomendacao'), texto: ` recomendação: ${o.recomendacao}` })
138
+ if (o.comando && o.comando !== o.recomendacao) linhas.push({ tipo: 'dim', chave: k('comando'), texto: ` comando: ${o.comando}` })
139
+ return linhas
140
+ }
141
+
142
+ /** As linhas do painel: o placar, o foco, as perguntas do lote, as decisoes por voce e as paradas. */
143
+ export function linhasDaMesa(e: EstadoDaMesa, opcoes: { agora: number }): LinhaDaMesa[] {
144
+ const { fila, foco } = e
145
+ const agora = opcoes.agora
146
+ const daThread = <T extends { thread: string | null }>(xs: readonly T[]): T[] => (foco ? xs.filter(x => x.thread === foco) : [...xs])
147
+ const perguntas = daThread(fila.perguntas)
148
+ const decisoes = daThread(fila.decisoes)
149
+ const outros = daThread(fila.outros)
150
+ const esperam = esperamODono({ ...fila, perguntas }, e.obvias)
151
+ const desde = esperam.map(p => Date.parse(p.desdeEm ?? '')).filter(Number.isFinite)
152
+ const novas = decisoes.filter(d => d.nova).length
153
+ const placar = [
154
+ `${plural(esperam.length, 'espera', 'esperam')} você${desde.length ? ` (a mais antiga há ${duracao(agora - Math.min(...desde))})` : ''}`,
155
+ `${plural(decisoes.length, 'decidida', 'decididas')} sem te chamar${novas ? ` (${plural(novas, 'nova', 'novas')})` : ''}`,
156
+ plural(outros.length, 'parada', 'paradas'),
157
+ fila.lidoEm === null ? 'ainda não li a fila' : `lido há ${duracao(agora - fila.lidoEm)}`,
158
+ ]
159
+ const linhas: LinhaDaMesa[] = [{ tipo: 'titulo', chave: 'titulo', texto: placar.join(' · ') }]
160
+ if (fila.erro) linhas.push({ tipo: 'aviso', chave: 'erro', texto: `não reli a fila: ${fila.erro}` })
161
+ fila.avisos.forEach((a, i) => linhas.push({ tipo: 'aviso', chave: `aviso:${i}`, texto: a }))
162
+ if (foco) linhas.push({ tipo: 'foco', chave: 'foco', texto: `só ${foco}` })
163
+
164
+ // A secao lista todo pedido aberto; o placar conta so os que esperam o dono (a decisao obvia nao espera).
165
+ linhas.push({ tipo: 'secao', chave: 'secao-perguntas', texto: 'Pedidos abertos' })
166
+ if (!perguntas.length) linhas.push({ tipo: 'dim', chave: 'sem-perguntas', texto: ' nenhum pedido aberto agora' })
167
+ const { pagina, paginas } = paginaDoLote(perguntas.length, e.pagina)
168
+ for (const p of perguntas.slice(pagina * TAMANHO_DO_LOTE, (pagina + 1) * TAMANHO_DO_LOTE)) linhas.push(...linhasDaPergunta(p, e, agora))
169
+ if (paginas > 1) linhas.push({ tipo: 'nav', chave: 'nav', texto: `lote ${pagina + 1} de ${paginas}` })
170
+
171
+ linhas.push({ tipo: 'secao', chave: 'secao-decisoes', texto: 'Decididas sem te chamar' })
172
+ if (!decisoes.length) linhas.push({ tipo: 'dim', chave: 'sem-decisoes', texto: ' nenhuma decisão tomada sem te chamar na janela do pulse' })
173
+ for (const d of decisoes.slice(0, MAXIMO_DE_DECISOES)) linhas.push(...linhasDaDecisao(d, agora))
174
+ if (decisoes.length > MAXIMO_DE_DECISOES) {
175
+ linhas.push({ tipo: 'dim', chave: 'mais-decisoes', texto: ` e mais ${decisoes.length - MAXIMO_DE_DECISOES} na janela do pulse (7 dias)` })
176
+ }
177
+
178
+ if (outros.length) {
179
+ linhas.push({ tipo: 'secao', chave: 'secao-outros', texto: 'Paradas e impedimentos' })
180
+ for (const o of outros.slice(0, MAXIMO_DE_OUTROS)) linhas.push(...linhasDoOutro(o, agora))
181
+ if (outros.length > MAXIMO_DE_OUTROS) linhas.push({ tipo: 'dim', chave: 'mais-outros', texto: ` e mais ${outros.length - MAXIMO_DE_OUTROS}` })
182
+ }
183
+ return linhas
184
+ }
185
+
186
+ /**
187
+ * O que o desenho poe na janela de rolagem e vai ao registro do mostrado: so linha com `mostrado` cuja posicao esta
188
+ * entre `offset` e `offset + bodyRows`. Sem janela, nada: o que nao estava na tela nao conta como visto.
189
+ */
190
+ export function mostradosVisiveis(linhas: readonly LinhaDaMesa[], janela: { offset: number; bodyRows: number } | undefined): MesaMostrado[] {
191
+ if (!janela || !(janela.bodyRows > 0)) return []
192
+ const fim = janela.offset + janela.bodyRows
193
+ return linhas.flatMap((l, i) => (l.mostrado && i >= janela.offset && i < fim ? [l.mostrado] : []))
194
+ }
195
+
196
+ export type AcoesDaMesa = {
197
+ decidir?: (pedidoId: string) => void
198
+ atualizar?: () => void
199
+ verTodas?: () => void
200
+ anteriores?: () => void
201
+ proximas?: () => void
202
+ }
203
+
204
+ /** A arvore do painel com os elementos que a superficie resolveu (`$.ui.resolve(e)` no register.tsx). */
205
+ export function desenharMesa(el: { Box: any; Text: any; Button: any }, linhas: readonly LinhaDaMesa[], acoes: AcoesDaMesa) {
206
+ const { Box, Text, Button } = el
207
+ return (
208
+ <Box flexDirection="column">
209
+ {linhas.map(l => {
210
+ if (l.tipo === 'titulo') {
211
+ return (
212
+ <Box key="titulo" flexDirection="row">
213
+ <Text key="placar" bold wrap="truncate">{l.texto}</Text>
214
+ <Button key="atualizar" label="Atualizar" onPress={() => acoes.atualizar?.()} />
215
+ </Box>
216
+ )
217
+ }
218
+ if (l.tipo === 'foco') {
219
+ return (
220
+ <Box key="foco" flexDirection="row">
221
+ <Text key="foco-texto" dimColor wrap="truncate">{l.texto}</Text>
222
+ <Button key="ver-todas" label="Ver todas" onPress={() => acoes.verTodas?.()} />
223
+ </Box>
224
+ )
225
+ }
226
+ if (l.tipo === 'nav') {
227
+ return (
228
+ <Box key="nav" flexDirection="row">
229
+ <Button key="anteriores" label="Anteriores" onPress={() => acoes.anteriores?.()} />
230
+ <Text key="nav-texto" dimColor wrap="truncate">{l.texto}</Text>
231
+ <Button key="proximas" label="Próximas" onPress={() => acoes.proximas?.()} />
232
+ </Box>
233
+ )
234
+ }
235
+ if (l.tipo === 'decidir') {
236
+ const pedidoId = l.pedidoId ?? ''
237
+ return <Button key={l.chave} label="Decidir" onPress={() => acoes.decidir?.(pedidoId)} />
238
+ }
239
+ if (l.tipo === 'secao' || l.tipo === 'cabecalho') return <Text key={l.chave} bold wrap="truncate">{l.texto}</Text>
240
+ if (l.tipo === 'aviso') return <Text key={l.chave} color="warning" wrap="truncate">{l.texto}</Text>
241
+ if (l.tipo === 'dim') return <Text key={l.chave} dimColor wrap="truncate">{l.texto}</Text>
242
+ return <Text key={l.chave} wrap="truncate">{l.texto}</Text>
243
+ })}
244
+ </Box>
245
+ )
246
+ }
@@ -0,0 +1,110 @@
1
+ // O que a Mesa do Dono manda ao nucleo (RM-059 F5; D6 e D7 da thread ork-rm059mesa), em funcoes puras. Sao so duas
2
+ // ferramentas com efeito e uma de leitura, todas no servidor `orkastery`:
3
+ // - `ork_request_decision` recebe exatamente `threadId` e `pedidoId`: o nucleo abre o dialogo nativo do host e grava
4
+ // a resposta que o dono der, com o recibo do ingresso local. A Mesa nunca manda opcao, letra nem texto.
5
+ // - `ork_decision_record` registra a decisao obvia (teste das quatro condicoes) como decisao autonoma do nucleo,
6
+ // com criterio, evidencia, como mudar e o custo de reverter. Nao responde o pedido.
7
+ // - `ork_hitl_pending` le os pedidos abertos de uma thread.
8
+ // So importa tipos.
9
+ import type { MesaDecidindo, MesaDecisao, MesaPergunta } from '../types'
10
+
11
+ export const SERVIDOR = 'orkastery'
12
+ /** A lista fechada de ferramentas do nucleo que a Mesa chama (D2 e D7). */
13
+ export const FERRAMENTAS_DA_MESA: readonly string[] = ['ork_hitl_pending', 'ork_request_decision', 'ork_decision_record']
14
+ /** Quem decidiu, no rastro do nucleo: a Mesa, nunca o dono (o nucleo recusa rastro que comeca pelo dono). */
15
+ export const QUEM_DECIDIU = 'mod ork-mesa na sessão do dono, pelo teste das quatro condições'
16
+ /** Os tetos do nucleo para a decisao informada (`TETOS_HITL_V2` e o esquema do `ork_decision_record`). */
17
+ export const TETO_DO_CAMPO = 200
18
+ export const TETO_DO_CUSTO = 140
19
+ export const TETO_DA_REFERENCIA = 300
20
+
21
+ /** Uma linha so, cortada no teto, para caber no contrato do nucleo. */
22
+ export function linhaNoTeto(v: string, teto: number): string {
23
+ const t = v.replace(/\s+/g, ' ').trim()
24
+ return t.length <= teto ? t : `${t.slice(0, teto - 1).trimEnd()}…`
25
+ }
26
+
27
+ /** Os argumentos do Decidir: so os dois ids, nada que seja resposta. */
28
+ export function argsDoDecidir(p: Pick<MesaPergunta, 'thread' | 'pedidoId'>): { threadId: string; pedidoId: string } {
29
+ return { threadId: p.thread, pedidoId: p.pedidoId }
30
+ }
31
+
32
+ /** A marca do pedido no texto da decisao obvia: e por ela que a Mesa sabe que ja decidiu este pedido. */
33
+ export function marcaDoPedido(pedidoId: string): string {
34
+ return `(${pedidoId.slice(0, 8)})`
35
+ }
36
+
37
+ /** Ja existe, na janela do pulse, a decisao obvia que a Mesa registrou para este pedido. */
38
+ export function jaDecidida(decisoes: readonly MesaDecisao[], p: Pick<MesaPergunta, 'thread' | 'pedidoId'>): boolean {
39
+ const marca = marcaDoPedido(p.pedidoId)
40
+ return decisoes.some(d => d.thread === p.thread && d.quemDecidiu.startsWith('mod ork-mesa') && d.decidido.includes(marca))
41
+ }
42
+
43
+ export type ArgsDaDecisaoObvia = {
44
+ threadId: string
45
+ decidido: string
46
+ porque: string
47
+ comoMudar: string
48
+ custoAgora: string
49
+ custoDepois: string
50
+ criterio: { tipo: 'medicao'; referencia: string }
51
+ quemDecidiu: string
52
+ evidencia: string
53
+ }
54
+
55
+ /**
56
+ * Os argumentos do `ork_decision_record` para uma decisao obvia: o que, o porque, como mudar, o custo de reverter
57
+ * antes e depois do prazo, o criterio (a medicao que mostra o pedido declarando `seguir-recomendada`) e a evidencia
58
+ * (a leitura do `ork_hitl_pending`). Cada campo cabe nos tetos do nucleo, numa linha.
59
+ */
60
+ export function argsDaDecisaoObvia(p: MesaPergunta, recomendada: { chave: string; texto: string }, referencia: string,
61
+ lidoEmIso: string): ArgsDaDecisaoObvia {
62
+ const pedido = p.codigo ?? p.pedidoId.slice(0, 8)
63
+ const prazo = p.prazoLocal ?? p.prazo ?? 'no prazo do pedido'
64
+ const reserva = p.reserva ? ` ou ${p.reserva} pelo Telegram` : ''
65
+ return {
66
+ threadId: p.thread,
67
+ decidido: linhaNoTeto(`Sem te chamar: o pedido ${pedido} ${marcaDoPedido(p.pedidoId)} da ${p.thread} segue a recomendada ` +
68
+ `${recomendada.chave} (${recomendada.texto}) no prazo ${prazo}`, TETO_DO_CAMPO),
69
+ porque: linhaNoTeto('O pedido declara que segue a recomendada no prazo, é reversível antes da entrega e passou nas quatro ' +
70
+ 'condições da decisão óbvia (M4)', TETO_DO_CAMPO),
71
+ comoMudar: linhaNoTeto(`Na Mesa, Decidir no cartão do pedido ${pedido} antes do prazo${reserva}`, TETO_DO_CAMPO),
72
+ custoAgora: linhaNoTeto('Nenhum: o pedido segue aberto até o prazo e o dono escolhe outra alternativa no diálogo', TETO_DO_CUSTO),
73
+ custoDepois: linhaNoTeto('Depois do prazo a recomendada já seguiu; mudar pede nova decisão na thread', TETO_DO_CUSTO),
74
+ criterio: { tipo: 'medicao', referencia: linhaNoTeto(referencia, TETO_DA_REFERENCIA) },
75
+ quemDecidiu: QUEM_DECIDIU,
76
+ evidencia: linhaNoTeto(`ork_hitl_pending ${p.thread} lido em ${lidoEmIso}: pedido ${p.pedidoId}, estado ${p.estado}, ` +
77
+ `acaoPadraoAoExpirar ${p.acaoAoExpirar ?? '-'}, irreversivel ${String(p.irreversivel)}, fase ${p.fase ?? '-'}`, 4096),
78
+ }
79
+ }
80
+
81
+ /**
82
+ * Depois de uma recarga do modulo nenhum Decidir esta em curso: o que ficou `abrindo` no estado vira `sem-resposta`,
83
+ * e o cartao volta a ter o botao.
84
+ */
85
+ export function soltarAbrindo(decidindo: Readonly<Record<string, MesaDecidindo>>): Record<string, MesaDecidindo> {
86
+ return Object.fromEntries(Object.entries(decidindo).map(([id, v]) => [id, v.estado === 'abrindo'
87
+ ? { estado: 'sem-resposta' as const, texto: 'o diálogo ficou sem resposta (a Mesa recarregou); o pedido segue aberto', em: v.em } : v]))
88
+ }
89
+
90
+ /** Por que a notificacao do sistema nao saiu, em pt-BR (o motor manda o codigo em ingles). */
91
+ export function motivoDaNotificacao(reason: unknown): string {
92
+ const motivos: Record<string, string> = {
93
+ 'no-channel': 'nenhum canal de notificação configurado',
94
+ 'no-surface': 'nenhuma superfície para mostrar',
95
+ refused: 'o terminal recusou a notificação',
96
+ disabled: 'as notificações estão desligadas',
97
+ }
98
+ return `a notificação do sistema não saiu (${motivos[String(reason)] ?? String(reason)})`
99
+ }
100
+
101
+ /** O que o cartao diz do Decidir, a partir do que o nucleo devolveu (`{ ok, pedidoId, estado, repetida }`). */
102
+ export function resultadoDoDecidir(resposta: unknown, agora: number): MesaDecidindo {
103
+ const r = (resposta && typeof resposta === 'object' ? resposta : {}) as { ok?: unknown; estado?: unknown; repetida?: unknown }
104
+ const estado = typeof r.estado === 'string' ? r.estado : ''
105
+ if (r.ok === true) {
106
+ if (r.repetida === true) return { estado: 'respondido', texto: `o pedido já estava respondido no núcleo: ${estado || 'registrado'}`, em: agora }
107
+ return { estado: 'respondido', texto: `respondido pelo diálogo: ${estado || 'registrado'}`, em: agora }
108
+ }
109
+ return { estado: 'sem-resposta', texto: 'o diálogo fechou sem resposta; o pedido segue aberto', em: agora }
110
+ }