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