@orkastery/cli 0.5.6 → 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.
- package/adapters/README.md +18 -0
- package/adapters/claude-code/.claude-plugin/plugin.json +1 -0
- package/adapters/claude-code/README.md +37 -5
- package/adapters/claude-code/agents/ork-check.md +1 -0
- package/adapters/claude-code/agents/ork-go.md +1 -0
- package/adapters/claude-code/agents/ork-goal.md +1 -0
- package/adapters/claude-code/agents/ork-master.md +1 -0
- package/adapters/claude-code/agents/ork-plan.md +1 -0
- package/adapters/claude-code/agents/ork-ship.md +1 -0
- package/adapters/claude-code/hooks/hooks.json +2 -1
- package/adapters/claude-code/hooks/ork-guard.js +15 -1
- package/adapters/claude-code/hooks/ork-resposta.js +91 -0
- package/adapters/claude-code/hooks/ork-sensor.js +18 -2
- package/adapters/claude-code/hooks/quota-signal.js +14 -0
- package/adapters/claude-code/mods/ork-mensageiro/.claude-plugin/plugin.json +14 -0
- package/adapters/claude-code/mods/ork-mensageiro/hooks/caixa.ts +215 -0
- package/adapters/claude-code/mods/ork-mensageiro/hooks/hooks.json +3 -0
- package/adapters/claude-code/mods/ork-mensageiro/hooks/painel.tsx +165 -0
- package/adapters/claude-code/mods/ork-mensageiro/hooks/recepcao.ts +256 -0
- package/adapters/claude-code/mods/ork-mensageiro/hooks/register.tsx +481 -0
- package/adapters/claude-code/mods/ork-mensageiro/orkastery-mod.json +5 -0
- package/adapters/claude-code/mods/ork-mensageiro/types/index.d.ts +85 -0
- package/adapters/claude-code/mods/ork-pista/hooks/ci.ts +9 -5
- package/adapters/claude-code/mods/ork-ritual/.claude-plugin/plugin.json +14 -0
- package/adapters/claude-code/mods/ork-ritual/hooks/hooks.json +3 -0
- package/adapters/claude-code/mods/ork-ritual/hooks/nota.ts +88 -0
- package/adapters/claude-code/mods/ork-ritual/hooks/register.tsx +494 -0
- package/adapters/claude-code/mods/ork-ritual/hooks/replay.tsx +271 -0
- package/adapters/claude-code/mods/ork-ritual/hooks/ritual.ts +218 -0
- package/adapters/claude-code/mods/ork-ritual/orkastery-mod.json +5 -0
- package/adapters/claude-code/mods/ork-ritual/types/index.d.ts +106 -0
- package/adapters/claude-code/mods/ork-sentinela/hooks/vigia.ts +1 -1
- package/adapters/codex/skills/ork/SKILL.md +2 -0
- package/adapters/hermes/README.md +64 -1
- package/adapters/hermes/bin/ork-conhecimento-colecoes.sh +14 -0
- package/adapters/hermes/bin/ork-conhecimento-listar.sh +15 -0
- package/adapters/hermes/bin/ork-conhecimento-mostrar.sh +16 -0
- package/adapters/hermes/bin/ork-conhecimento-verificar.sh +15 -0
- package/adapters/hermes/bin/ork-vigia-batida.py +127 -0
- package/adapters/hermes/hermes.plugin.json +4 -0
- package/adapters/hermes/hitl-ingress/__init__.py +101 -8
- package/adapters/hermes/skills/orkastery-devmaster/SKILL.md +2 -2
- package/adapters/openclaw/README.md +81 -9
- package/adapters/openclaw/bin/ork-vigia-batida.js +86 -0
- package/adapters/openclaw/dist/hitl-ingress.js +106 -9
- package/adapters/openclaw/dist/index.js +86 -0
- package/adapters/openclaw/openclaw.plugin.json +34 -4
- package/adapters/openclaw/package.json +2 -2
- package/adapters/openclaw/src/hitl-ingress.ts +103 -9
- package/adapters/openclaw/src/index.ts +94 -0
- package/assets/adapters-en/claude-code/README.md +403 -0
- package/assets/adapters-en/claude-code/agents/ork-check.md +33 -0
- package/assets/adapters-en/claude-code/agents/ork-go.md +33 -0
- package/assets/adapters-en/claude-code/agents/ork-goal.md +34 -0
- package/assets/adapters-en/claude-code/agents/ork-master.md +35 -0
- package/assets/adapters-en/claude-code/agents/ork-plan.md +33 -0
- package/assets/adapters-en/claude-code/agents/ork-ship.md +33 -0
- package/assets/adapters-en/claude-code/commands/check.md +46 -0
- package/assets/adapters-en/claude-code/commands/go.md +46 -0
- package/assets/adapters-en/claude-code/commands/goal.md +55 -0
- package/assets/adapters-en/claude-code/commands/master.md +54 -0
- package/assets/adapters-en/claude-code/commands/onboarding.md +22 -0
- package/assets/adapters-en/claude-code/commands/ork.md +126 -0
- package/assets/adapters-en/claude-code/commands/plan.md +47 -0
- package/assets/adapters-en/claude-code/commands/ship.md +45 -0
- package/assets/adapters-en/codex/skills/ork/SKILL.md +85 -0
- package/assets/adapters-en/hermes/README.md +273 -0
- package/assets/adapters-en/hermes/skills/orkastery-devmaster/SKILL.md +169 -0
- package/assets/references-en/README.md +25 -0
- package/assets/references-en/code-review-axes.md +82 -0
- package/assets/references-en/definition-of-done.md +74 -0
- package/assets/references-en/performance-checklist.md +53 -0
- package/assets/references-en/security-checklist.md +101 -0
- package/assets/references-en/testing-patterns.md +51 -0
- package/assets/security-scan/defaults/.golangci.yml +2 -0
- package/assets/security-scan/defaults/eslint.config.mjs +11 -0
- package/assets/security-scan/defaults/gitleaks.toml +3 -0
- package/assets/security-scan/defaults/ruff.toml +1 -0
- package/assets/security-scan/tools-agil.json +87 -0
- package/assets/security-scan/tools.json +625 -0
- package/assets/skills-en/core/onboarding/SKILL.md +58 -0
- package/assets/skills-en/core/orkastery-bootstrap/SKILL.md +119 -0
- package/assets/skills-en/core/thread-state/SKILL.md +88 -0
- package/assets/skills-en/governance/decision-triage/SKILL.md +65 -0
- package/assets/skills-en/governance/narrative-guardian/SKILL.md +66 -0
- package/assets/skills-en/governance/roadmap-keeper/SKILL.md +60 -0
- package/assets/skills-en/governance/scope-check-capability-map/SKILL.md +71 -0
- package/assets/skills-en/observability/thread-tracing/SKILL.md +63 -0
- package/assets/skills-en/phases/check-quality/SKILL.md +103 -0
- package/assets/skills-en/phases/go-implementation/SKILL.md +78 -0
- package/assets/skills-en/phases/goal-definition/SKILL.md +69 -0
- package/assets/skills-en/phases/master-metrics/SKILL.md +72 -0
- package/assets/skills-en/phases/plan-specification/SKILL.md +67 -0
- package/assets/skills-en/phases/security-remediation/SKILL.md +70 -0
- package/assets/skills-en/phases/ship-release/SKILL.md +71 -0
- package/assets/skills-en/reviewers/code-reviewer/SKILL.md +61 -0
- package/assets/skills-en/reviewers/security-auditor/SKILL.md +93 -0
- package/assets/skills-en/reviewers/test-engineer/SKILL.md +66 -0
- package/assets/skills-en/reviewers/web-performance-auditor/SKILL.md +63 -0
- package/dist/adapters/claude-bg.js +118 -4
- package/dist/adapters/codex-controller-worker.js +12 -5
- package/dist/adapters/codex-events.js +16 -4
- package/dist/adapters/codex.js +3 -1
- package/dist/atualizar-branch.js +245 -0
- package/dist/auditoria.js +13 -11
- package/dist/auditrun.js +51 -46
- package/dist/autorizacao-duravel.js +327 -0
- package/dist/board.js +94 -40
- package/dist/branch-de-estado.js +16 -8
- package/dist/busca-semantica.js +7 -5
- package/dist/catalogo.js +2 -1
- package/dist/changelog-reconciliacao.js +290 -0
- package/dist/check-agil.js +500 -0
- package/dist/check-modo.js +41 -0
- package/dist/check-paralelo.js +183 -0
- package/dist/ci.js +248 -45
- package/dist/ciclos.js +8 -6
- package/dist/claim-lint.js +12 -8
- package/dist/claims-conferir.js +47 -39
- package/dist/claims.js +10 -9
- package/dist/company-brain-client.js +10 -3
- package/dist/company-brain-migration.js +11 -5
- package/dist/conducao-batida.js +759 -0
- package/dist/conducao-sentinela.js +25 -8
- package/dist/conducao.js +84 -29
- package/dist/conhecimento-consulta.js +270 -0
- package/dist/conhecimento.js +111 -87
- package/dist/conta-esgotada.js +127 -0
- package/dist/dado-privado.js +816 -0
- package/dist/decisao-autonoma.js +5 -4
- package/dist/demo.js +14 -12
- package/dist/dependencias-da-worktree.js +25 -0
- package/dist/divida.js +38 -36
- package/dist/docs.js +81 -74
- package/dist/doctor-forja.js +345 -0
- package/dist/doctor-pulse-cron.js +113 -1
- package/dist/doctor.js +164 -6
- package/dist/dsn-da-memoria.js +417 -0
- package/dist/entrega-por-mod.js +503 -0
- package/dist/entrega-pr.js +143 -43
- package/dist/esgotamentos.js +13 -11
- package/dist/estado-thread.js +21 -18
- package/dist/evalrunner.js +35 -32
- package/dist/fabrica-batida.js +34 -21
- package/dist/fabrica-estado.js +13 -10
- package/dist/fix.js +21 -19
- package/dist/forja/adaptador.js +6 -0
- package/dist/forja/ambiente-de-ci.js +27 -0
- package/dist/forja/api.js +256 -0
- package/dist/forja/cache.js +89 -0
- package/dist/forja/cli.js +60 -0
- package/dist/forja/contrato.js +9 -0
- package/dist/forja/endereco.js +43 -0
- package/dist/forja/estado.js +22 -0
- package/dist/forja/executor.js +73 -0
- package/dist/forja/github/ci.js +95 -0
- package/dist/forja/github/dono-da-rede.js +18 -0
- package/dist/forja/github/metadados.js +15 -0
- package/dist/forja/github/pedidos.js +200 -0
- package/dist/forja/gitlab/ci.js +141 -0
- package/dist/forja/gitlab/consultas.js +59 -0
- package/dist/forja/gitlab/dono-da-rede.js +43 -0
- package/dist/forja/gitlab/metadados.js +15 -0
- package/dist/forja/gitlab/pedidos-abertos.js +158 -0
- package/dist/forja/gitlab/pedidos.js +141 -0
- package/dist/forja/produtor.js +32 -0
- package/dist/forja/registro.js +31 -0
- package/dist/forja/resolver.js +121 -0
- package/dist/forja/texto.js +13 -0
- package/dist/forja/versao.js +71 -0
- package/dist/forja.js +123 -80
- package/dist/handoff.js +23 -13
- package/dist/hitl-contract.js +46 -43
- package/dist/hitl-curto.js +37 -25
- package/dist/hitl-gates.js +13 -11
- package/dist/hitl-local-receipt.js +51 -0
- package/dist/hitl-local.js +11 -2
- package/dist/hitl-lock.js +5 -11
- package/dist/hitl-public-receipt.js +11 -0
- package/dist/hitl-resumo.js +44 -1
- package/dist/hitl-sessions.js +62 -9
- package/dist/hitl-texto-livre.js +7 -4
- package/dist/hitl.js +34 -31
- package/dist/horario.js +16 -0
- package/dist/hosts.js +104 -134
- package/dist/index.js +848 -548
- package/dist/indice-vetorial.js +14 -12
- package/dist/init.js +11 -2
- package/dist/integracoes-locais.js +2 -0
- package/dist/intelligence-graph-cli.js +123 -69
- package/dist/intelligence-graph-query.js +37 -22
- package/dist/leases.js +19 -18
- package/dist/ledger-stats.js +17 -16
- package/dist/ledger.js +31 -1
- package/dist/licoes.js +12 -11
- package/dist/locale-accounts.js +220 -0
- package/dist/locale-adapter.js +291 -0
- package/dist/locale-ajuda.js +76 -4
- package/dist/locale-audit.js +199 -0
- package/dist/locale-claims.js +213 -0
- package/dist/locale-conducao.js +138 -0
- package/dist/locale-conhecimento.js +181 -0
- package/dist/locale-decisao.js +205 -0
- package/dist/locale-docs.js +230 -0
- package/dist/locale-estado.js +101 -0
- package/dist/locale-forja.js +290 -0
- package/dist/locale-gerados.js +5 -0
- package/dist/locale-grafo.js +129 -0
- package/dist/locale-master.js +83 -0
- package/dist/locale-network.js +524 -0
- package/dist/locale-operacao-restante.js +931 -0
- package/dist/locale-projetos.js +100 -0
- package/dist/locale-prompts.js +194 -0
- package/dist/locale-pulse.js +140 -2
- package/dist/locale-reservas.js +87 -0
- package/dist/locale-retry.js +145 -6
- package/dist/locale-security-scan.js +69 -0
- package/dist/locale-setup.js +192 -0
- package/dist/locale-ship.js +169 -66
- package/dist/locale-thread.js +149 -5
- package/dist/locale-verify.js +80 -4
- package/dist/locale-worktree.js +246 -0
- package/dist/locale.js +553 -9
- package/dist/maestro-runtime.js +22 -3
- package/dist/manifest.js +287 -11
- package/dist/maquina-ociosa.js +211 -0
- package/dist/maquina.js +10 -5
- package/dist/master-nota-local.js +200 -0
- package/dist/master-nota.js +10 -2
- package/dist/master.js +118 -41
- package/dist/mcp-conhecimento.js +52 -0
- package/dist/mcp-experiencia.js +26 -5
- package/dist/mcp-install.js +73 -8
- package/dist/mcp-noctum.js +101 -0
- package/dist/mcp-server.js +82 -14
- package/dist/mcp-ship-transporte.js +977 -0
- package/dist/mcp-ship.js +68 -24
- package/dist/mcp-transportes.js +17 -0
- package/dist/memoria.js +63 -58
- package/dist/modos-migracao.js +8 -6
- package/dist/modos.js +17 -18
- package/dist/network-roadmap.js +85 -49
- package/dist/noctum-aceite.js +789 -0
- package/dist/noctum-arco.js +540 -33
- package/dist/noctum-capacidade.js +3 -3
- package/dist/noctum-changelog.js +399 -0
- package/dist/noctum-cli.js +482 -0
- package/dist/noctum-coordenacao.js +1225 -0
- package/dist/noctum-entrega.js +1169 -0
- package/dist/noctum-envelope.js +252 -16
- package/dist/noctum-execucao.js +95 -23
- package/dist/noctum-fechamento.js +405 -0
- package/dist/noctum-fila.js +43 -9
- package/dist/noctum-guarda.js +256 -0
- package/dist/noctum-integracao.js +507 -0
- package/dist/noctum-maquinas.js +383 -0
- package/dist/noctum-medicao.js +450 -0
- package/dist/noctum-modo.js +362 -0
- package/dist/noctum-pergunta-canal.js +96 -0
- package/dist/noctum-pergunta.js +115 -0
- package/dist/noctum-provas.js +838 -0
- package/dist/noctum-reconciliacao.js +591 -0
- package/dist/noctum-registro.js +215 -0
- package/dist/noctum-relatorio.js +827 -0
- package/dist/noctum-resposta.js +207 -0
- package/dist/noctum-sessoes-em-voo.js +93 -0
- package/dist/noctum-supervisao.js +649 -0
- package/dist/noctum-terminal.js +459 -0
- package/dist/noctum-transporte.js +655 -0
- package/dist/noctum-vigia.js +639 -0
- package/dist/ocupacao.js +3 -1
- package/dist/onboarding.js +16 -5
- package/dist/orkmind.js +39 -23
- package/dist/orquestracao.js +31 -35
- package/dist/parado-no-condutor.js +146 -124
- package/dist/phase.js +53 -27
- package/dist/policies.js +33 -0
- package/dist/postura-local.js +4 -3
- package/dist/procedencia.js +9 -14
- package/dist/projeto-alvo.js +59 -20
- package/dist/prompts.js +136 -42
- package/dist/prova-minima.js +5 -6
- package/dist/pulse-cadencia.js +5 -4
- package/dist/pulse-consentimento.js +53 -1
- package/dist/pulse-delivery.js +143 -27
- package/dist/pulse-resposta.js +368 -4
- package/dist/pulse.js +136 -3
- package/dist/ratelimit.js +12 -4
- package/dist/recall.js +14 -13
- package/dist/rede-adesao.js +13 -8
- package/dist/rede-forja.js +108 -24
- package/dist/rede-status.js +63 -45
- package/dist/rede.js +86 -46
- package/dist/retomada-checkpoint.js +101 -0
- package/dist/retry.js +228 -73
- package/dist/roadmap-reservas.js +32 -32
- package/dist/runtime-ambiente.js +5 -0
- package/dist/runtime-context.js +117 -43
- package/dist/runtime-escada.js +423 -0
- package/dist/runtime-profiles.js +103 -46
- package/dist/saida-segura.js +3 -2
- package/dist/security-scan/agil.js +355 -0
- package/dist/security-scan/cli.js +76 -0
- package/dist/security-scan/compare.js +149 -0
- package/dist/security-scan/config.js +93 -0
- package/dist/security-scan/detect.js +97 -0
- package/dist/security-scan/executor.js +188 -0
- package/dist/security-scan/guard.js +180 -0
- package/dist/security-scan/imagem-de-lint.js +96 -0
- package/dist/security-scan/normalize/actionlint.js +12 -0
- package/dist/security-scan/normalize/eslint.js +12 -0
- package/dist/security-scan/normalize/gitleaks.js +13 -0
- package/dist/security-scan/normalize/golangci-lint.js +13 -0
- package/dist/security-scan/normalize/govulncheck.js +36 -0
- package/dist/security-scan/normalize/index.js +25 -0
- package/dist/security-scan/normalize/npm-audit.js +30 -0
- package/dist/security-scan/normalize/osv-scanner.js +32 -0
- package/dist/security-scan/normalize/oxlint.js +51 -0
- package/dist/security-scan/normalize/pip-audit.js +16 -0
- package/dist/security-scan/normalize/ruff.js +12 -0
- package/dist/security-scan/normalize/semgrep.js +20 -0
- package/dist/security-scan/normalize/trivy.js +33 -0
- package/dist/security-scan/normalize/util.js +204 -0
- package/dist/security-scan/normalize/zizmor.js +19 -0
- package/dist/security-scan/report.js +156 -0
- package/dist/security-scan/run.js +448 -0
- package/dist/security-scan/tools.js +62 -0
- package/dist/security-scan/trabalhador.js +74 -0
- package/dist/security-scan/types.js +15 -0
- package/dist/session-events.js +19 -2
- package/dist/session-watcher-claude.js +74 -19
- package/dist/session-watcher.js +156 -10
- package/dist/sessoes-contas.js +9 -0
- package/dist/sessoes-inventario.js +1 -1
- package/dist/sessoes.js +25 -23
- package/dist/setup.js +59 -58
- package/dist/ship-publicar.js +263 -92
- package/dist/ship.js +160 -17
- package/dist/superficie.js +14 -11
- package/dist/thread-close.js +9 -9
- package/dist/thread.js +121 -18
- package/dist/tokens.js +15 -11
- package/dist/trabalho-thread.js +297 -0
- package/dist/transporte-do-canal.js +189 -0
- package/dist/types.js +12 -1
- package/dist/util.js +5 -2
- package/dist/varredura-acoes.js +507 -0
- package/dist/verify.js +472 -29
- package/dist/worktree-local.js +12 -10
- package/dist/worktree.js +81 -85
- package/eval/casos/check-quality.json +77 -0
- package/eval/casos/security-auditor.json +148 -0
- package/eval/casos/security-remediation.json +161 -0
- package/eval/fixtures/security-scan/README.md +8 -0
- package/eval/fixtures/security-scan/go/.github/workflows/ci.yml +12 -0
- package/eval/fixtures/security-scan/go/Dockerfile +4 -0
- package/eval/fixtures/security-scan/go/README.md +4 -0
- package/eval/fixtures/security-scan/go/config.go +4 -0
- package/eval/fixtures/security-scan/go/go.mod +5 -0
- package/eval/fixtures/security-scan/go/go.sum +2 -0
- package/eval/fixtures/security-scan/go/main.go +31 -0
- package/eval/fixtures/security-scan/js/.github/workflows/ci.yml +12 -0
- package/eval/fixtures/security-scan/js/Dockerfile +3 -0
- package/eval/fixtures/security-scan/js/README.md +4 -0
- package/eval/fixtures/security-scan/js/package-lock.json +844 -0
- package/eval/fixtures/security-scan/js/package.json +10 -0
- package/eval/fixtures/security-scan/js/src/config.js +4 -0
- package/eval/fixtures/security-scan/js/src/server.js +17 -0
- package/eval/fixtures/security-scan/js/src/util.ts +4 -0
- package/eval/fixtures/security-scan/js/tsconfig.json +4 -0
- package/eval/fixtures/security-scan/python/.github/workflows/ci.yml +12 -0
- package/eval/fixtures/security-scan/python/Dockerfile +3 -0
- package/eval/fixtures/security-scan/python/README.md +4 -0
- package/eval/fixtures/security-scan/python/app/__init__.py +0 -0
- package/eval/fixtures/security-scan/python/app/config.py +2 -0
- package/eval/fixtures/security-scan/python/app/main.py +19 -0
- package/eval/fixtures/security-scan/python/requirements.txt +1 -0
- package/package.json +9 -4
- package/references/security-checklist.md +3 -3
- package/skills/README.md +1 -0
- package/skills/core/orkastery-bootstrap/SKILL.md +2 -2
- package/skills/phases/check-quality/SKILL.md +16 -0
- package/skills/phases/go-implementation/SKILL.md +6 -0
- package/skills/phases/security-remediation/SKILL.md +70 -0
- package/skills/reviewers/security-auditor/SKILL.md +25 -0
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
// O painel do Ritual do MASTER (RM-059, fatia F8, T8.2): a lista das entregas que esperam a nota do dono e o replay da
|
|
2
|
+
// prova de uma delas, uma claim por vez, como o replay-theater percorre edicoes. Modulo puro: monta as linhas e a
|
|
3
|
+
// arvore com os elementos que a superficie resolveu; o `$` fica no register.tsx. Estado sempre com rotulo e glifo,
|
|
4
|
+
// nunca so cor; "espera sua nota" e o ponto de HITL do cartao; numero vem com o comando que o prova (Partitura).
|
|
5
|
+
import type { RitualEntrega, RitualMostrado, RitualNota, RitualProva, RitualQuadro, RitualReserva } from '../types'
|
|
6
|
+
import { pedeReserva } from './nota'
|
|
7
|
+
import { JANELA_DIAS, numero, plural } from './ritual'
|
|
8
|
+
|
|
9
|
+
/** Os botoes de uma entrega, na linha de acoes do cartao. */
|
|
10
|
+
export type BotaoDoCartao = 'prova' | 'nota' | 'telegram'
|
|
11
|
+
|
|
12
|
+
/** Uma linha do painel: o tipo decide o elemento; `mostrado` e o que o registro da ponte guarda (M5). */
|
|
13
|
+
export type LinhaDoPainel = {
|
|
14
|
+
tipo: 'placar' | 'aviso' | 'feita' | 'vazio' | 'cabecalho' | 'situacao' | 'indice' | 'postmortem' | 'acoes' | 'nota' | 'reserva'
|
|
15
|
+
| 'prova-placar' | 'claim' | 'alegacao' | 'comando' | 'navegacao'
|
|
16
|
+
texto: string
|
|
17
|
+
thread?: string
|
|
18
|
+
/** So na linha de acoes: os botoes que ela tem, na ordem. */
|
|
19
|
+
botoes?: BotaoDoCartao[]
|
|
20
|
+
mostrado?: RitualMostrado
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Os rotulos dos botoes do cartao. */
|
|
24
|
+
export const ROTULO_DO_BOTAO: Readonly<Record<BotaoDoCartao, string>> = { prova: 'Ver a prova', nota: 'Dar a nota', telegram: 'Pelo Telegram' }
|
|
25
|
+
|
|
26
|
+
/** O glifo e o rotulo de cada estado de claim que o nucleo registra. */
|
|
27
|
+
export const ESTADO_DA_CLAIM: Readonly<Record<string, { glifo: string; rotulo: string }>> = {
|
|
28
|
+
verificado: { glifo: '✓', rotulo: 'verificada' },
|
|
29
|
+
reprovado: { glifo: '×', rotulo: 'reprovada' },
|
|
30
|
+
pendente: { glifo: '·', rotulo: 'pendente' },
|
|
31
|
+
'nao-verificavel': { glifo: '?', rotulo: 'não verificável' },
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Quanto tempo faz a leitura, em palavras curtas. */
|
|
35
|
+
export function haQuanto(desde: number | null, agora: number): string {
|
|
36
|
+
if (desde === null) return 'ainda não lido'
|
|
37
|
+
const s = Math.max(0, Math.round((agora - desde) / 1000))
|
|
38
|
+
if (s < 60) return `lido há ${s} s`
|
|
39
|
+
const min = Math.round(s / 60)
|
|
40
|
+
return min < 60 ? `lido há ${min} min` : `lido há ${Math.round(min / 60)} h`
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** A situacao da nota na entrega, em uma linha. */
|
|
44
|
+
export function situacaoDaEntrega(e: RitualEntrega): string {
|
|
45
|
+
if (e.status === 'recorded') return `aceita por omissão${e.score === null ? '' : ` com ${e.score}/5`}; a sua nota sobrescreve`
|
|
46
|
+
if (e.status === 'ratificacao-pendente') return 'proposta da fábrica esperando a sua nota'
|
|
47
|
+
return 'sem nota: sem a sua, o MASTER fecha por omissão'
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** O cartao de uma entrega: cabecalho, situacao, indice e POSTMORTEM. */
|
|
51
|
+
export function linhasDaEntrega(e: RitualEntrega): LinhaDoPainel[] {
|
|
52
|
+
const quando = e.entregueEmLocal ?? e.entregueEm
|
|
53
|
+
const merge = e.merge ? ` · merge ${e.merge.slice(0, 8)}` : e.artefato ? ` · artefato ${e.artefato}` : ''
|
|
54
|
+
const indice = e.indice === null ? 'índice derivado do ledger: lacuna (ork master)'
|
|
55
|
+
: `índice derivado do ledger: ${numero(e.indice)} de 5 (${e.parcelas || 'sem desconto'}) · ork master`
|
|
56
|
+
const reprovacoes = plural(e.reprovacoes, 'reprovação tipada', 'reprovações tipadas')
|
|
57
|
+
return [
|
|
58
|
+
{ tipo: 'cabecalho', thread: e.thread, texto: `● espera sua nota · ${e.thread} · entregue ${quando}${merge}` },
|
|
59
|
+
{ tipo: 'situacao', thread: e.thread, texto: ` ${situacaoDaEntrega(e)}` },
|
|
60
|
+
{ tipo: 'indice', thread: e.thread, texto: ` ${indice}`,
|
|
61
|
+
mostrado: { chave: `nota:${e.thread}`, thread: e.thread,
|
|
62
|
+
texto: `${e.thread}: entregue, espera sua nota${e.indice === null ? '' : ` (índice ${numero(e.indice)} de 5)`}` } },
|
|
63
|
+
// Na proposta, as classes sao as que a fabrica propos; a nota do dono grava as que o nucleo infere dos gates, como
|
|
64
|
+
// no terminal e no Telegram (D14).
|
|
65
|
+
{ tipo: 'postmortem', thread: e.thread, texto: e.classes
|
|
66
|
+
? ` POSTMORTEM${e.status === 'ratificacao-pendente' ? ' da proposta' : ''}: classes ${e.classes} · ${reprovacoes}`
|
|
67
|
+
: ` POSTMORTEM: nasce no fechamento · ${reprovacoes}` },
|
|
68
|
+
]
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* A linha de acoes e o resultado do "Dar a nota" e da reserva de uma entrega (T8.3). O "Dar a nota" some com o dialogo
|
|
73
|
+
* aberto e com a nota ja registrada; a reserva pelo Telegram so aparece quando o dialogo nao serviu (D7).
|
|
74
|
+
*/
|
|
75
|
+
export function linhasDaNota(thread: string, opcoes: { comProva: boolean; nota?: RitualNota; reserva?: RitualReserva }): LinhaDoPainel[] {
|
|
76
|
+
const { nota, reserva } = opcoes
|
|
77
|
+
const botoes: BotaoDoCartao[] = []
|
|
78
|
+
if (opcoes.comProva) botoes.push('prova')
|
|
79
|
+
if (!nota || !['abrindo', 'registrada', 'repetida'].includes(nota.estado)) botoes.push('nota')
|
|
80
|
+
if (pedeReserva(nota) && reserva?.estado !== 'pedindo') botoes.push('telegram')
|
|
81
|
+
const saida: LinhaDoPainel[] = []
|
|
82
|
+
if (botoes.length) saida.push({ tipo: 'acoes', thread, botoes, texto: botoes.map(b => ROTULO_DO_BOTAO[b]).join(' | ') })
|
|
83
|
+
if (nota) saida.push({ tipo: 'nota', thread, texto: ` ${nota.estado === 'abrindo' ? 'diálogo da nota aberto no Claude Code: responda lá' : nota.texto}` })
|
|
84
|
+
if (reserva) {
|
|
85
|
+
saida.push({ tipo: 'reserva', thread, texto: ` ${reserva.estado === 'pedindo' ? 'pedindo o código ao núcleo...' : reserva.texto}`,
|
|
86
|
+
...(reserva.estado === 'pronta' && reserva.codigo
|
|
87
|
+
? { mostrado: { chave: `nota:${thread}`, thread, texto: `${thread}: pelo Telegram, responda ${reserva.codigo} <0 a 5> <porquê>` } } : {}) })
|
|
88
|
+
}
|
|
89
|
+
return saida
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** As notas desta sessao que ja sairam da lista (registradas ou ja dadas), as mais novas primeiro, no maximo tres. */
|
|
93
|
+
export function notasFeitas(q: RitualQuadro, notas: Readonly<Record<string, RitualNota>>): LinhaDoPainel[] {
|
|
94
|
+
const naLista = new Set(q.entregas.map(e => e.thread))
|
|
95
|
+
return Object.entries(notas).filter(([t, n]) => (n.estado === 'registrada' || n.estado === 'repetida') && !naLista.has(t))
|
|
96
|
+
.sort((a, b) => b[1].em - a[1].em).slice(0, 3)
|
|
97
|
+
.map(([thread, n]) => ({ tipo: 'feita' as const, thread, texto: `✓ ${n.texto}` }))
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** As linhas do replay: a claim do passo, com estado, arquivo, alegacao e comandos; nunca a saida do comando (D6). */
|
|
101
|
+
export function linhasDaProva(foco: string, prova: RitualProva | null, passo: number): LinhaDoPainel[] {
|
|
102
|
+
if (!prova || prova.thread !== foco || (prova.lidoEm === null && !prova.erro)) return [{ tipo: 'vazio', thread: foco, texto: 'lendo as claims no núcleo...' }]
|
|
103
|
+
const saida: LinhaDoPainel[] = []
|
|
104
|
+
if (prova.erro) saida.push({ tipo: 'aviso', thread: foco, texto: `as claims não vieram: ${prova.erro}` })
|
|
105
|
+
if (!prova.claims.length) {
|
|
106
|
+
if (!prova.erro) saida.push({ tipo: 'vazio', thread: foco, texto: `Nenhuma claim ativa registrada na ${foco}.` })
|
|
107
|
+
return saida
|
|
108
|
+
}
|
|
109
|
+
const i = Math.min(Math.max(0, passo), prova.claims.length - 1)
|
|
110
|
+
const c = prova.claims[i]!
|
|
111
|
+
const estado = ESTADO_DA_CLAIM[c.estado] ?? { glifo: '?', rotulo: c.estado }
|
|
112
|
+
const retiradas = prova.retiradas ? ` · ${plural(prova.retiradas, 'retirada fora', 'retiradas fora')}` : ''
|
|
113
|
+
saida.push({ tipo: 'prova-placar', thread: foco, texto: ` claim ${i + 1} de ${prova.claims.length}${retiradas} · ork claims list ${foco}` })
|
|
114
|
+
saida.push({ tipo: 'claim', thread: foco, texto: ` ${estado.glifo} ${c.id} · ${estado.rotulo}${c.arquivo ? ` · ${c.arquivo}` : ''}` })
|
|
115
|
+
if (c.alegacao) saida.push({ tipo: 'alegacao', thread: foco, texto: ` ${c.alegacao}` })
|
|
116
|
+
for (const comando of c.comandos) saida.push({ tipo: 'comando', thread: foco, texto: ` $ ${comando}` })
|
|
117
|
+
if (!c.comandos.length) saida.push({ tipo: 'comando', thread: foco, texto: ' sem comando registrado' })
|
|
118
|
+
saida.push({ tipo: 'navegacao', thread: foco, texto: `${i > 0 ? 'anterior' : ''}|${i < prova.claims.length - 1 ? 'proxima' : ''}` })
|
|
119
|
+
return saida
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* As linhas do painel, na ordem do desenho. Sem foco: o placar, os avisos e um cartao por entrega com "Ver a prova".
|
|
124
|
+
* Com foco (`/nota <thread>` ou "Ver a prova"): o placar do replay, o cartao da thread e a claim do passo.
|
|
125
|
+
*/
|
|
126
|
+
export function linhasDoPainel(q: RitualQuadro, opcoes: { agora: number; foco?: string | null; prova?: RitualProva | null; passo?: number;
|
|
127
|
+
notas?: Readonly<Record<string, RitualNota>>; reservas?: Readonly<Record<string, RitualReserva>> }): LinhaDoPainel[] {
|
|
128
|
+
const saida: LinhaDoPainel[] = []
|
|
129
|
+
const foco = opcoes.foco ?? null
|
|
130
|
+
const notas = opcoes.notas ?? {}
|
|
131
|
+
const reservas = opcoes.reservas ?? {}
|
|
132
|
+
const quantas = q.semLeitura && !q.entregas.length ? `o retrato não leu ${plural(q.semLeitura, 'thread', 'threads')}`
|
|
133
|
+
: `${q.semLeitura ? 'pelo menos ' : ''}${plural(q.entregas.length, 'entrega espera', 'entregas esperam')} a sua nota`
|
|
134
|
+
saida.push({ tipo: 'placar', texto: foco ? `Ritual do MASTER · prova da ${foco}` : `Ritual do MASTER · ${quantas} · ${haQuanto(q.lidoEm, opcoes.agora)}` })
|
|
135
|
+
if (q.erro) saida.push({ tipo: 'aviso', texto: `a última leitura falhou: ${q.erro}` })
|
|
136
|
+
for (const aviso of q.avisos) saida.push({ tipo: 'aviso', texto: aviso })
|
|
137
|
+
saida.push(...notasFeitas(q, notas))
|
|
138
|
+
if (foco) {
|
|
139
|
+
const e = q.entregas.find(x => x.thread === foco)
|
|
140
|
+
if (e) saida.push(...linhasDaEntrega(e), ...linhasDaNota(foco, { comProva: false, nota: notas[foco], reserva: reservas[foco] }))
|
|
141
|
+
else if (q.lidoEm !== null) saida.push({ tipo: 'aviso', thread: foco, texto: `a ${foco} não está entre as entregas dos últimos ${JANELA_DIAS} dias que esperam a sua nota` })
|
|
142
|
+
saida.push(...linhasDaProva(foco, opcoes.prova ?? null, opcoes.passo ?? 0))
|
|
143
|
+
return saida
|
|
144
|
+
}
|
|
145
|
+
if (q.foraDaJanela) {
|
|
146
|
+
saida.push({ tipo: 'aviso', texto: `${plural(q.foraDaJanela, 'entrega sem a sua nota ficou', 'entregas sem a sua nota ficaram')} fora da janela de ${JANELA_DIAS} dias (ork master)` })
|
|
147
|
+
}
|
|
148
|
+
if (!q.entregas.length) {
|
|
149
|
+
saida.push({ tipo: 'vazio', texto: q.lidoEm === null ? 'lendo as entregas no núcleo...'
|
|
150
|
+
: q.semLeitura ? `Nenhuma das entregas que o retrato leu espera a sua nota nos últimos ${JANELA_DIAS} dias; ork master lista as das threads que ele não leu.`
|
|
151
|
+
: `Nada espera a sua nota nos últimos ${JANELA_DIAS} dias.` })
|
|
152
|
+
return saida
|
|
153
|
+
}
|
|
154
|
+
for (const e of q.entregas) {
|
|
155
|
+
saida.push(...linhasDaEntrega(e), ...linhasDaNota(e.thread, { comProva: true, nota: notas[e.thread], reserva: reservas[e.thread] }))
|
|
156
|
+
}
|
|
157
|
+
return saida
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* As linhas da tela que um texto com `wrap="wrap"` ocupa em `colunas` celulas: quebra por palavra, e a palavra maior que
|
|
162
|
+
* a linha quebra no meio. A `folga` tira colunas da conta: com ela a estimativa sobra.
|
|
163
|
+
*/
|
|
164
|
+
export function linhasDaTela(texto: string, colunas: number | undefined, folga = 1): number {
|
|
165
|
+
if (typeof colunas !== 'number' || !(colunas > folga)) return 1
|
|
166
|
+
const largura = colunas - folga
|
|
167
|
+
let linhas = 1
|
|
168
|
+
let usadas = 0
|
|
169
|
+
texto.split(' ').forEach((palavra, i) => {
|
|
170
|
+
let tamanho = Array.from(palavra).length
|
|
171
|
+
const espaco = i === 0 ? 0 : 1
|
|
172
|
+
if (usadas + espaco + tamanho <= largura) {
|
|
173
|
+
usadas += espaco + tamanho
|
|
174
|
+
return
|
|
175
|
+
}
|
|
176
|
+
if (usadas > 0) {
|
|
177
|
+
linhas++
|
|
178
|
+
usadas = 0
|
|
179
|
+
}
|
|
180
|
+
while (tamanho > largura) {
|
|
181
|
+
linhas++
|
|
182
|
+
tamanho -= largura
|
|
183
|
+
}
|
|
184
|
+
usadas = tamanho
|
|
185
|
+
})
|
|
186
|
+
return linhas
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/** A altura de uma linha do painel na tela, sem e com a folga: so a alegacao quebra; as outras truncam numa so. */
|
|
190
|
+
function alturasNaTela(l: { tipo?: string; texto?: string }, colunas: number | undefined): [number, number] {
|
|
191
|
+
if (l.tipo !== 'alegacao') return [1, 1]
|
|
192
|
+
return [linhasDaTela(l.texto ?? '', colunas, 0), linhasDaTela(l.texto ?? '', colunas, 1)]
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* O que esta inteiro dentro da janela de rolagem do painel: so isso conta como mostrado ao dono (M5). A linha que quebra
|
|
197
|
+
* tem o topo mais cedo (sem folga) e o mais tarde (com folga): ela so conta com os dois dentro da janela.
|
|
198
|
+
*/
|
|
199
|
+
export function mostradosVisiveis(linhas: ReadonlyArray<{ tipo?: string; texto?: string; mostrado?: RitualMostrado }>,
|
|
200
|
+
janela: { offset: number; bodyRows: number } | undefined, colunas?: number): RitualMostrado[] {
|
|
201
|
+
if (!janela || !(janela.bodyRows > 0)) return []
|
|
202
|
+
const fim = janela.offset + janela.bodyRows
|
|
203
|
+
const vistos: RitualMostrado[] = []
|
|
204
|
+
let cedo = 0
|
|
205
|
+
let tarde = 0
|
|
206
|
+
for (const l of linhas) {
|
|
207
|
+
const [menor, maior] = alturasNaTela(l, colunas)
|
|
208
|
+
if (l.mostrado && cedo >= janela.offset && tarde + maior <= fim) vistos.push(l.mostrado)
|
|
209
|
+
cedo += menor
|
|
210
|
+
tarde += maior
|
|
211
|
+
}
|
|
212
|
+
return vistos
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/** Os gestos do painel: reler, abrir e fechar o replay, andar entre as claims, pedir a nota e a reserva. */
|
|
216
|
+
export type Gestos = {
|
|
217
|
+
atualizar?: () => void
|
|
218
|
+
verProva?: (thread: string) => void
|
|
219
|
+
voltar?: () => void
|
|
220
|
+
anterior?: () => void
|
|
221
|
+
proxima?: () => void
|
|
222
|
+
darNota?: (thread: string) => void
|
|
223
|
+
telegram?: (thread: string) => void
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* A arvore do painel com os elementos que a superficie resolveu (no register.tsx), uma linha da tela por elemento: so
|
|
228
|
+
* `Box`, `Text` e `Button`, no terminal e no desktop.
|
|
229
|
+
*/
|
|
230
|
+
export function desenharRitual(el: { Box: any; Text: any; Button: any }, linhas: readonly LinhaDoPainel[], opcoes: { foco?: string | null } & Gestos) {
|
|
231
|
+
const { Box, Text, Button } = el
|
|
232
|
+
return (
|
|
233
|
+
<Box flexDirection="column">
|
|
234
|
+
{linhas.map((l, i) => {
|
|
235
|
+
if (l.tipo === 'placar') {
|
|
236
|
+
return (
|
|
237
|
+
<Box key="placar" flexDirection="row">
|
|
238
|
+
<Text key="placar-texto" bold wrap="truncate">{l.texto}</Text>
|
|
239
|
+
{opcoes.foco ? <Button key="voltar" label="Voltar" onPress={() => opcoes.voltar?.()} /> : null}
|
|
240
|
+
<Button key="atualizar" label="Atualizar" onPress={() => opcoes.atualizar?.()} />
|
|
241
|
+
</Box>
|
|
242
|
+
)
|
|
243
|
+
}
|
|
244
|
+
if (l.tipo === 'cabecalho') return <Text key={`cabecalho:${l.thread}`} bold wrap="truncate">{l.texto}</Text>
|
|
245
|
+
if (l.tipo === 'acoes' && l.thread) {
|
|
246
|
+
const thread = l.thread
|
|
247
|
+
const gesto: Record<BotaoDoCartao, ((t: string) => void) | undefined> = { prova: opcoes.verProva, nota: opcoes.darNota, telegram: opcoes.telegram }
|
|
248
|
+
return (
|
|
249
|
+
<Box key={`acoes:${thread}`} flexDirection="row">
|
|
250
|
+
{(l.botoes ?? []).map(b => <Button key={`${b}:${thread}`} label={ROTULO_DO_BOTAO[b]} onPress={() => gesto[b]?.(thread)} />)}
|
|
251
|
+
</Box>
|
|
252
|
+
)
|
|
253
|
+
}
|
|
254
|
+
if (l.tipo === 'feita') return <Text key={`feita:${l.thread}`} wrap="truncate">{l.texto}</Text>
|
|
255
|
+
if (l.tipo === 'navegacao') {
|
|
256
|
+
const [anterior, proxima] = l.texto.split('|')
|
|
257
|
+
return (
|
|
258
|
+
<Box key="navegacao" flexDirection="row">
|
|
259
|
+
{anterior ? <Button key="anterior" label="Anterior" onPress={() => opcoes.anterior?.()} /> : null}
|
|
260
|
+
{proxima ? <Button key="proxima" label="Próxima" onPress={() => opcoes.proxima?.()} /> : null}
|
|
261
|
+
</Box>
|
|
262
|
+
)
|
|
263
|
+
}
|
|
264
|
+
if (l.tipo === 'claim') return <Text key="claim" bold wrap="truncate">{l.texto}</Text>
|
|
265
|
+
if (l.tipo === 'alegacao') return <Text key="alegacao" wrap="wrap">{l.texto}</Text>
|
|
266
|
+
if (l.tipo === 'aviso') return <Text key={`aviso:${i}`} color="warning" wrap="truncate">{l.texto}</Text>
|
|
267
|
+
return <Text key={`${l.tipo}:${l.thread ?? ''}:${i}`} dimColor={l.tipo !== 'comando'} wrap="truncate">{l.texto}</Text>
|
|
268
|
+
})}
|
|
269
|
+
</Box>
|
|
270
|
+
)
|
|
271
|
+
}
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
// O que o Ritual do MASTER le do nucleo (RM-059, fatia F8; ficha M8 do plano dos mods), em funcoes puras: as entregas
|
|
2
|
+
// que esperam a nota do dono, saidas da secao `master` do retrato da ponte (D2 da thread ork-rm059ritual), a
|
|
3
|
+
// linha da faixa e as claims do replay, saidas do `ork_claims_list`. Nada aqui le `.orkastery/` nem chama o `$`: as
|
|
4
|
+
// chamadas ficam no register.tsx. So importa tipos.
|
|
5
|
+
import type { RitualClaim, RitualEntrega, RitualNota, RitualQuadro } from '../types'
|
|
6
|
+
|
|
7
|
+
/** D1: a entrega dos ultimos 7 dias sem nota humana (sem MASTER, com proposta ou aceita por omissao). */
|
|
8
|
+
export const JANELA_DIAS = 7
|
|
9
|
+
/** A secao `master` vem em paginas de ate 50 itens; mais que isso e retrato quebrado, nao fila. */
|
|
10
|
+
export const TETO_DE_PAGINAS = 20
|
|
11
|
+
/** O painel mostra no maximo isto; o resto vira aviso com a contagem. */
|
|
12
|
+
export const TETO_DE_ENTREGAS = 40
|
|
13
|
+
export const TETO_DE_TEXTO = 200
|
|
14
|
+
export const SERVIDOR = 'orkastery'
|
|
15
|
+
export const QUADRO_VAZIO: RitualQuadro = { entregas: [], lidoEm: null, erro: null, avisos: [], foraDaJanela: 0, semLeitura: 0 }
|
|
16
|
+
|
|
17
|
+
const ID_DE_THREAD = /^[a-zA-Z0-9][a-zA-Z0-9._-]{0,79}$/
|
|
18
|
+
const DIA_MS = 86400000
|
|
19
|
+
|
|
20
|
+
/** Uma linha so, sem caractere de controle e com teto. */
|
|
21
|
+
export function umaLinha(v: unknown, teto = TETO_DE_TEXTO): string {
|
|
22
|
+
const s = typeof v === 'string' ? v : v instanceof Error ? v.message : String(v)
|
|
23
|
+
return Array.from(s.replace(/\p{Cc}/gu, ' ').replace(/\s+/g, ' ').trim()).slice(0, teto).join('')
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function plural(n: number, um: string, varios: string): string {
|
|
27
|
+
return `${n} ${n === 1 ? um : varios}`
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function exigirThread(thread: unknown): string {
|
|
31
|
+
if (typeof thread !== 'string' || !ID_DE_THREAD.test(thread)) throw new Error('ork-ritual: id de thread invalido')
|
|
32
|
+
return thread
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** O numero com virgula, como o dono le: 3.75 vira "3,75". */
|
|
36
|
+
export function numero(n: number): string {
|
|
37
|
+
return String(n).replace('.', ',')
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** O JSON do primeiro bloco de texto de uma resposta MCP; a resposta com erro vira erro com o motivo do nucleo. */
|
|
41
|
+
export function jsonDoMcp(r: { content?: ReadonlyArray<{ type?: string; text?: string }>; isError?: boolean } | null | undefined): unknown {
|
|
42
|
+
const texto = (r?.content ?? []).find(c => c.type === 'text' && typeof c.text === 'string')?.text
|
|
43
|
+
if (!r || r.isError) {
|
|
44
|
+
let motivo = 'o núcleo respondeu com erro'
|
|
45
|
+
try {
|
|
46
|
+
const corpo = JSON.parse(texto ?? '') as { erro?: unknown }
|
|
47
|
+
if (typeof corpo.erro === 'string' && corpo.erro.trim()) motivo = umaLinha(corpo.erro)
|
|
48
|
+
} catch {
|
|
49
|
+
// Sem JSON no corpo do erro: fica o motivo generico.
|
|
50
|
+
}
|
|
51
|
+
throw new Error(motivo)
|
|
52
|
+
}
|
|
53
|
+
if (texto === undefined) throw new Error('o núcleo não devolveu texto')
|
|
54
|
+
return JSON.parse(texto)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Uma pagina da secao `master` do retrato: os itens, a proxima pagina e as lacunas. */
|
|
58
|
+
export function paginaDoMaster(r: unknown): { itens: unknown[]; proxima: number | null; lacunas: string[] } {
|
|
59
|
+
const secao = (r as { retrato?: { sections?: { master?: unknown } } } | null)?.retrato?.sections?.master as
|
|
60
|
+
{ items?: unknown; gaps?: unknown; coverage?: { nextOffset?: unknown } } | undefined
|
|
61
|
+
if (!secao || !Array.isArray(secao.items)) throw new Error('o retrato do núcleo não trouxe a seção master')
|
|
62
|
+
const proxima = secao.coverage?.nextOffset
|
|
63
|
+
return {
|
|
64
|
+
itens: secao.items as unknown[],
|
|
65
|
+
proxima: typeof proxima === 'number' && Number.isInteger(proxima) && proxima >= 0 ? proxima : null,
|
|
66
|
+
lacunas: Array.isArray(secao.gaps) ? (secao.gaps as unknown[]).filter((g): g is string => typeof g === 'string').map(g => umaLinha(g, 120)) : [],
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const textoOuNulo = (v: unknown, teto = TETO_DE_TEXTO): string | null => (typeof v === 'string' && v.trim() ? umaLinha(v, teto) : null)
|
|
71
|
+
const numeroOuNulo = (v: unknown): number | null => (typeof v === 'number' && Number.isFinite(v) ? v : null)
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* A entrega de um item da secao `master`, quando ela espera a nota do dono: sem MASTER (`pending`), com proposta
|
|
75
|
+
* (`ratificacao-pendente`) ou aceita por omissao (`recorded` com regime `omissao`), entregue por `ship_done` ou por
|
|
76
|
+
* artefato. Nota humana registrada, item sem entrega ou fora do contrato devolvem null.
|
|
77
|
+
*/
|
|
78
|
+
export function entregaDoItem(item: unknown): RitualEntrega | null {
|
|
79
|
+
const i = (item ?? {}) as { id?: unknown; status?: unknown; facts?: Record<string, unknown> }
|
|
80
|
+
const m = typeof i.id === 'string' ? /^master:(.+)$/.exec(i.id) : null
|
|
81
|
+
const thread = m && ID_DE_THREAD.test(m[1] as string) ? m[1] as string : null
|
|
82
|
+
const f = i.facts ?? {}
|
|
83
|
+
if (!thread || (i.status !== 'pending' && i.status !== 'ratificacao-pendente' && i.status !== 'recorded')) return null
|
|
84
|
+
if (i.status === 'recorded' && f.regime !== 'omissao') return null
|
|
85
|
+
const entregueEm = typeof f.deliveredAt === 'string' && Number.isFinite(Date.parse(f.deliveredAt)) ? f.deliveredAt : null
|
|
86
|
+
if (!entregueEm) return null
|
|
87
|
+
return {
|
|
88
|
+
thread, status: i.status, regime: textoOuNulo(f.regime, 40), score: numeroOuNulo(f.score), indice: numeroOuNulo(f.index),
|
|
89
|
+
parcelas: textoOuNulo(f.indexDetail) ?? '', reprovacoes: numeroOuNulo(f.reprovals) ?? 0, classes: textoOuNulo(f.classes),
|
|
90
|
+
entregueEm, entregueEmLocal: textoOuNulo(f.deliveredAtLocal, 80),
|
|
91
|
+
merge: typeof f.deliveredSha === 'string' && /^[a-f0-9]{40}$/.test(f.deliveredSha) ? f.deliveredSha : null,
|
|
92
|
+
artefato: textoOuNulo(f.deliveredArtifact, 160),
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** As entregas da janela, a mais nova primeiro, com o teto do painel; e quantas ficaram fora da janela. */
|
|
97
|
+
export function entregasDaJanela(itens: readonly unknown[], agora: number, dias = JANELA_DIAS): { entregas: RitualEntrega[]; fora: number; alemDoTeto: number } {
|
|
98
|
+
const todas = itens.map(entregaDoItem).filter((e): e is RitualEntrega => e !== null)
|
|
99
|
+
const desde = agora - dias * DIA_MS
|
|
100
|
+
const dentro = todas.filter(e => Date.parse(e.entregueEm) >= desde)
|
|
101
|
+
.sort((a, b) => Date.parse(b.entregueEm) - Date.parse(a.entregueEm) || (a.thread < b.thread ? -1 : 1))
|
|
102
|
+
return { entregas: dentro.slice(0, TETO_DE_ENTREGAS), fora: todas.length - dentro.length, alemDoTeto: Math.max(0, dentro.length - TETO_DE_ENTREGAS) }
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** A lacuna que nao falta nada: o corte da pagina por bytes (a proxima pagina traz o resto, e o Ritual le todas). */
|
|
106
|
+
const SO_PAGINACAO = new Set(['maestro.page.byte_limit'])
|
|
107
|
+
const OMITIDAS = /^maestro\.messages\.omitted:(\d+)$/
|
|
108
|
+
const SEM_LEITURA = /^(?:ledger\.unavailable|master\.invalid):([a-zA-Z0-9][a-zA-Z0-9._-]{0,79})$/
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* As lacunas da secao `master`, somadas de todas as paginas (cada pagina repete as da secao): as threads que o retrato
|
|
112
|
+
* nao leu (o ledger passou do teto de leitura do Maestro, ou o MASTER log nao vale), quantas lacunas o retrato omitiu
|
|
113
|
+
* alem das quatro que publica (na secao `master` elas sao por thread) e as outras. O corte da pagina por bytes nao
|
|
114
|
+
* falta nada.
|
|
115
|
+
*/
|
|
116
|
+
export function lacunasDoMaster(lacunas: readonly string[]): { semLeitura: string[]; omitidas: number; outras: string[] } {
|
|
117
|
+
const semLeitura: string[] = []
|
|
118
|
+
const outras: string[] = []
|
|
119
|
+
let omitidas = 0
|
|
120
|
+
for (const lacuna of new Set(lacunas)) {
|
|
121
|
+
if (SO_PAGINACAO.has(lacuna)) continue
|
|
122
|
+
const omitida = OMITIDAS.exec(lacuna)
|
|
123
|
+
if (omitida) {
|
|
124
|
+
omitidas = Math.max(omitidas, Number(omitida[1]))
|
|
125
|
+
continue
|
|
126
|
+
}
|
|
127
|
+
const thread = SEM_LEITURA.exec(lacuna)?.[1]
|
|
128
|
+
if (thread) {
|
|
129
|
+
if (!semLeitura.includes(thread)) semLeitura.push(thread)
|
|
130
|
+
continue
|
|
131
|
+
}
|
|
132
|
+
outras.push(lacuna)
|
|
133
|
+
}
|
|
134
|
+
return { semLeitura, omitidas, outras }
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** Quantas threads o retrato nao leu: as nomeadas nas lacunas e as omitidas. */
|
|
138
|
+
export function threadsSemLeitura(lacunas: readonly string[]): number {
|
|
139
|
+
const { semLeitura, omitidas } = lacunasDoMaster(lacunas)
|
|
140
|
+
return semLeitura.length + omitidas
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** Os avisos da secao que veio incompleta, uma linha cada: as threads que o retrato nao leu e as outras lacunas. */
|
|
144
|
+
export function avisosDasLacunas(lacunas: readonly string[]): string[] {
|
|
145
|
+
const { semLeitura, omitidas, outras } = lacunasDoMaster(lacunas)
|
|
146
|
+
const avisos: string[] = []
|
|
147
|
+
const sem = semLeitura.length + omitidas
|
|
148
|
+
if (sem) {
|
|
149
|
+
const nomes = semLeitura.slice(0, 3)
|
|
150
|
+
const resto = sem - nomes.length
|
|
151
|
+
const quais = nomes.length ? ` (${nomes.join(', ')}${resto > 0 ? ` e +${resto}` : ''})` : ''
|
|
152
|
+
avisos.push(`o retrato não leu ${plural(sem, 'thread', 'threads')}${quais}: a lista pode estar incompleta; ork master lista todas`)
|
|
153
|
+
}
|
|
154
|
+
if (outras.length) {
|
|
155
|
+
avisos.push(`o retrato do MASTER veio incompleto: ${plural(outras.length, 'lacuna', 'lacunas')} (${outras.slice(0, 3).join(', ')}${outras.length > 3 ? ', ...' : ''})`)
|
|
156
|
+
}
|
|
157
|
+
return avisos
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* A entrega cuja nota o nucleo registrou nesta sessao (ou que ja tinha nota humana) sai da lista na hora: a pagina que a
|
|
162
|
+
* ponte guarda por ate 60 s ainda pode lista-la. A entrega mais nova que a nota volta a esperar.
|
|
163
|
+
*/
|
|
164
|
+
export function semNotaDada(entregas: readonly RitualEntrega[], notas: Readonly<Record<string, RitualNota>>): RitualEntrega[] {
|
|
165
|
+
return entregas.filter(e => {
|
|
166
|
+
const n = notas[e.thread]
|
|
167
|
+
return !n || (n.estado !== 'registrada' && n.estado !== 'repetida') || n.em < Date.parse(e.entregueEm)
|
|
168
|
+
})
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* A linha da faixa: quantas entregas esperam a nota e quais; sem nenhuma (ou antes da primeira leitura), sem linha. Com
|
|
173
|
+
* thread que o retrato nao leu, a contagem e "pelo menos", e sem entrega visivel a linha aponta o `ork master`.
|
|
174
|
+
*/
|
|
175
|
+
export function textoDaFaixa(q: RitualQuadro): string | undefined {
|
|
176
|
+
if (q.lidoEm === null) return undefined
|
|
177
|
+
if (!q.entregas.length) {
|
|
178
|
+
return q.semLeitura ? `o retrato não leu ${plural(q.semLeitura, 'thread', 'threads')}: ork master lista as entregas que esperam a sua nota · /nota` : undefined
|
|
179
|
+
}
|
|
180
|
+
const nomes = q.entregas.slice(0, 3).map(e => e.thread)
|
|
181
|
+
const resto = q.entregas.length - nomes.length
|
|
182
|
+
return `${q.semLeitura ? 'pelo menos ' : ''}${plural(q.entregas.length, 'entrega espera', 'entregas esperam')} a sua nota: ${nomes.join(', ')}${resto > 0 ? ` e +${resto}` : ''} · /nota`
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/** A thread do ledger que mudou (`classic.FileChanged`), quando o caminho e o ledger de uma thread. */
|
|
186
|
+
export function threadDoLedger(caminho: unknown): string | null {
|
|
187
|
+
if (typeof caminho !== 'string') return null
|
|
188
|
+
const m = /\/\.orkastery\/threads\/([a-zA-Z0-9][a-zA-Z0-9._-]{0,79})\/ledger\.jsonl$/.exec(caminho)
|
|
189
|
+
return m ? m[1] as string : null
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/** Os argumentos do `ork_claims_list`: so o id da thread. */
|
|
193
|
+
export function argsDasClaims(thread: string): { threadId: string } {
|
|
194
|
+
return { threadId: exigirThread(thread) }
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const numeroDoId = (id: string) => { const m = /^C(\d+)$/.exec(id); return m ? Number(m[1]) : Number.POSITIVE_INFINITY }
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* As claims do `ork_claims_list` (o arquivo e so de acrescimo: a retirada e uma linha nova): o ultimo registro de cada
|
|
201
|
+
* id, as retiradas contadas a parte, na ordem dos ids. Registro fora do contrato fica de fora. Nunca a saida do comando:
|
|
202
|
+
* o nucleo nao a devolve aqui, e o replay nao a mostra (D6).
|
|
203
|
+
*/
|
|
204
|
+
export function claimsDaProva(resposta: unknown): { claims: RitualClaim[]; retiradas: number } {
|
|
205
|
+
if (!Array.isArray(resposta)) throw new Error('o ork_claims_list não devolveu a lista de claims')
|
|
206
|
+
const ultimas = new Map<string, RitualClaim>()
|
|
207
|
+
for (const bruto of resposta as unknown[]) {
|
|
208
|
+
const c = (bruto ?? {}) as Record<string, unknown>
|
|
209
|
+
if (typeof c.id !== 'string' || !/^C\d+$/.test(c.id) || typeof c.estado !== 'string') continue
|
|
210
|
+
ultimas.set(c.id, {
|
|
211
|
+
id: c.id, estado: umaLinha(c.estado, 40), arquivo: typeof c.arquivo === 'string' ? umaLinha(c.arquivo, 300) : '',
|
|
212
|
+
alegacao: typeof c.alegacao === 'string' ? umaLinha(c.alegacao, 1200) : '',
|
|
213
|
+
comandos: Array.isArray(c.verificar) ? (c.verificar as unknown[]).filter((v): v is string => typeof v === 'string').map(v => umaLinha(v, 600)) : [],
|
|
214
|
+
})
|
|
215
|
+
}
|
|
216
|
+
const todas = [...ultimas.values()].sort((a, b) => numeroDoId(a.id) - numeroDoId(b.id) || (a.id < b.id ? -1 : 1))
|
|
217
|
+
return { claims: todas.filter(c => c.estado !== 'retirada'), retiradas: todas.filter(c => c.estado === 'retirada').length }
|
|
218
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
// Contrato do estado do Ritual do MASTER (`ork-ritual`, RM-059, fatia F8; ficha M8 do plano dos mods). O motor exige
|
|
2
|
+
// que cada chave de `$.state` do mod esteja declarada aqui, em `PluginState`. O Ritual nao acrescenta noun ao `$`: le as
|
|
3
|
+
// entregas pelo retrato da `ork-ponte` (`$.ork.retrato`, secao `master`) e as claims pelo `ork_claims_list` do nucleo.
|
|
4
|
+
// Nenhum campo guarda o porque do dono, e a nota so aparece como o texto do que o nucleo registrou: quem recebe e grava
|
|
5
|
+
// a nota e o nucleo, pelo dialogo nativo do host. Sem import nem referencia.
|
|
6
|
+
|
|
7
|
+
/** Uma entrega que espera a nota do dono, como a secao `master` do retrato do nucleo a da. */
|
|
8
|
+
export type RitualEntrega = {
|
|
9
|
+
thread: string
|
|
10
|
+
/** `pending` (sem MASTER), `ratificacao-pendente` (proposta) ou `recorded` (so a aceita por omissao entra aqui). */
|
|
11
|
+
status: 'pending' | 'ratificacao-pendente' | 'recorded'
|
|
12
|
+
/** O regime do MASTER log: `omissao` na aceita por omissao; null sem MASTER log. */
|
|
13
|
+
regime: string | null
|
|
14
|
+
/** O score que a omissao gravou (o indice arredondado), quando ha. */
|
|
15
|
+
score: number | null
|
|
16
|
+
/** O indice derivado do ledger, de 0 a 5, e as parcelas que o descontaram. */
|
|
17
|
+
indice: number | null
|
|
18
|
+
parcelas: string
|
|
19
|
+
/** Os gates que barraram a thread (reprovacoes tipadas), fora os de ensaio. */
|
|
20
|
+
reprovacoes: number
|
|
21
|
+
/** As classes de falha do MASTER log (ou da proposta); null quando o POSTMORTEM ainda nao nasceu. */
|
|
22
|
+
classes: string | null
|
|
23
|
+
/** A entrega (`ship_done` ou artefato), em ISO e na hora local que o nucleo formatou, e o merge. */
|
|
24
|
+
entregueEm: string
|
|
25
|
+
entregueEmLocal: string | null
|
|
26
|
+
merge: string | null
|
|
27
|
+
/** O arquivo da entrega por artefato (a thread so de planejamento); ausente no quadro gravado antes deste campo. */
|
|
28
|
+
artefato?: string | null
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** A ultima leitura das entregas. */
|
|
32
|
+
export type RitualQuadro = {
|
|
33
|
+
entregas: RitualEntrega[]
|
|
34
|
+
/** Quando foi lida (ms desde a epoca); null antes da primeira leitura. */
|
|
35
|
+
lidoEm: number | null
|
|
36
|
+
/** Por que a ultima leitura falhou, quando falhou (a anterior continua no quadro). */
|
|
37
|
+
erro: string | null
|
|
38
|
+
/** O que veio incompleto do retrato (lacunas da secao, paginas que sobraram). */
|
|
39
|
+
avisos: string[]
|
|
40
|
+
/** Quantas entregas sem nota humana ficaram fora da janela de dias. */
|
|
41
|
+
foraDaJanela: number
|
|
42
|
+
/**
|
|
43
|
+
* Quantas threads o retrato nao leu (o ledger passou do teto de leitura do Maestro, ou o MASTER log nao vale): com
|
|
44
|
+
* alguma, a contagem da faixa e "pelo menos". Ausente no quadro gravado antes deste campo.
|
|
45
|
+
*/
|
|
46
|
+
semLeitura?: number
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Uma claim no replay: o ultimo registro de cada id, como o `ork_claims_list` a da. Nunca a saida do comando. */
|
|
50
|
+
export type RitualClaim = {
|
|
51
|
+
id: string
|
|
52
|
+
estado: string
|
|
53
|
+
arquivo: string
|
|
54
|
+
alegacao: string
|
|
55
|
+
comandos: string[]
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** A prova de uma thread no replay. */
|
|
59
|
+
export type RitualProva = {
|
|
60
|
+
thread: string
|
|
61
|
+
/** As claims ativas, na ordem dos ids. */
|
|
62
|
+
claims: RitualClaim[]
|
|
63
|
+
retiradas: number
|
|
64
|
+
lidoEm: number | null
|
|
65
|
+
erro: string | null
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** O que o painel mostrou ao dono e o Telegram tambem diria: a chave junta o que e a mesma coisa (M5). */
|
|
69
|
+
export type RitualMostrado = { chave: string; thread: string; texto: string }
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* O "Dar a nota" de uma entrega (T8.3): o dialogo pedido ao nucleo e o que ele devolveu. So o estado e o texto do
|
|
73
|
+
* resultado; a nota e o porque que o dono deu ficam no nucleo, nunca aqui.
|
|
74
|
+
*/
|
|
75
|
+
export type RitualNota = {
|
|
76
|
+
estado: 'abrindo' | 'registrada' | 'repetida' | 'pendente' | 'sem-dialogo' | 'erro'
|
|
77
|
+
texto: string
|
|
78
|
+
em: number
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** A reserva pelo Telegram (T8.3): o pedido de nota do nucleo e o codigo que o dono responde por la. */
|
|
82
|
+
export type RitualReserva = {
|
|
83
|
+
estado: 'pedindo' | 'pronta' | 'erro'
|
|
84
|
+
texto: string
|
|
85
|
+
codigo: string | null
|
|
86
|
+
em: number
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
declare module 'claude-code' {
|
|
90
|
+
interface PluginState {
|
|
91
|
+
'ork-ritual': {
|
|
92
|
+
/** A ultima leitura das entregas. */
|
|
93
|
+
quadro: RitualQuadro
|
|
94
|
+
/** A thread no replay da prova, pedida por "Ver a prova" ou por `/nota <thread>`. */
|
|
95
|
+
foco: string | null
|
|
96
|
+
/** A claim que o replay mostra, a partir de 0. */
|
|
97
|
+
passo: number
|
|
98
|
+
/** As claims da thread em foco. */
|
|
99
|
+
prova: RitualProva | null
|
|
100
|
+
/** O "Dar a nota" de cada entrega, pelo id da thread. */
|
|
101
|
+
notas: Record<string, RitualNota>
|
|
102
|
+
/** A reserva pelo Telegram de cada entrega, pelo id da thread. */
|
|
103
|
+
reservas: Record<string, RitualReserva>
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
@@ -165,7 +165,7 @@ export function textoDoDiario(r: SentinelaResumo, diario: readonly SentinelaEntr
|
|
|
165
165
|
const linhas: string[] = []
|
|
166
166
|
linhas.push(pausada ? 'Sentinela pausada: o condutor externo conduz. Para voltar: /sentinela retomar.'
|
|
167
167
|
: r.presenca === 'outra' ? 'Outra sessão conduz pela Sentinela nesta máquina; esta só olha.'
|
|
168
|
-
: r.presenca === 'minha' ? 'Sentinela conduzindo nesta máquina: o condutor externo cede o despacho e o retry.'
|
|
168
|
+
: r.presenca === 'minha' ? 'Sentinela conduzindo nesta máquina: o condutor externo cede o despacho e o retry, menos a retomada por cota.'
|
|
169
169
|
: 'Sentinela ligada, sem a presença confirmada pelo núcleo ainda.')
|
|
170
170
|
if (r.erro) linhas.push(`Última leitura do núcleo falhou: ${r.erro}`)
|
|
171
171
|
if (r.passos.length > 0) {
|
|
@@ -44,6 +44,8 @@ O dono conversa sobre o objetivo; voce opera o nucleo `ork` e apresenta o result
|
|
|
44
44
|
passo. Preserve o modo e as autorizacoes da thread. Se ja recebeu uma fase do Orkastery,
|
|
45
45
|
execute essa fase no escopo recebido, sem redespachar a si mesmo.
|
|
46
46
|
|
|
47
|
+
- Esperas: nunca dispare verify, build ou suite com `&` dentro do comando, porque o processo se perde e a sessao espera um aviso que nunca vem; rode o `ork verify` em primeiro plano, com `timeout` (ele espera a vez na fila de recursos pesados), ou use a execucao em segundo plano do host, que avisa quando termina; nunca espere com `pgrep -f` cujo padrao aparece no proprio comando, porque o laco acha a si mesmo e nao termina: espere pelo arquivo de saida com prazo, ou pelo PID guardado.
|
|
48
|
+
|
|
47
49
|
Quando descobertas, prefira as ferramentas MCP vinculadas ao projeto pelo servidor:
|
|
48
50
|
`ork_thread_status`, `ork_phase_list`, `ork_hitl_pending`, `ork_thread_new(nome, modo)`,
|
|
49
51
|
`ork_phase_run(threadId, fase, prompt, runtime?, model?, effort?, dryRun?)`, `ork_observe(threadId)`
|