@orkastery/cli 0.2.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/LICENSE +21 -0
- package/README.md +87 -0
- package/adapters/README.md +22 -0
- package/adapters/claude-code/.claude-plugin/marketplace.json +15 -0
- package/adapters/claude-code/.claude-plugin/plugin.json +31 -0
- package/adapters/claude-code/README.md +102 -0
- package/adapters/claude-code/agents/ork-check.md +32 -0
- package/adapters/claude-code/agents/ork-go.md +32 -0
- package/adapters/claude-code/agents/ork-goal.md +33 -0
- package/adapters/claude-code/agents/ork-master.md +32 -0
- package/adapters/claude-code/agents/ork-plan.md +32 -0
- package/adapters/claude-code/agents/ork-ship.md +32 -0
- package/adapters/claude-code/commands/check.md +45 -0
- package/adapters/claude-code/commands/go.md +46 -0
- package/adapters/claude-code/commands/goal.md +54 -0
- package/adapters/claude-code/commands/master.md +42 -0
- package/adapters/claude-code/commands/ork.md +37 -0
- package/adapters/claude-code/commands/plan.md +47 -0
- package/adapters/claude-code/commands/ship.md +45 -0
- package/adapters/claude-code/hooks/hooks.json +17 -0
- package/adapters/claude-code/hooks/ork-guard.js +130 -0
- package/adapters/hermes/README.md +45 -0
- package/adapters/hermes/bin/ork-abrir-thread.sh +25 -0
- package/adapters/hermes/hermes.plugin.json +21 -0
- package/adapters/hermes/skills/orkastery-devmaster/SKILL.md +116 -0
- package/adapters/openclaw/README.md +63 -0
- package/adapters/openclaw/bin/ork-abrir-thread.sh +24 -0
- package/adapters/openclaw/openclaw.plugin.json +138 -0
- package/dist/adapters/claude-bg.js +308 -0
- package/dist/auditoria.js +848 -0
- package/dist/auditrun.js +976 -0
- package/dist/board.js +314 -0
- package/dist/canarios.js +271 -0
- package/dist/catalogo.js +124 -0
- package/dist/ciclos.js +156 -0
- package/dist/claims.js +226 -0
- package/dist/divida.js +374 -0
- package/dist/doctor.js +246 -0
- package/dist/evalrunner.js +458 -0
- package/dist/fix.js +450 -0
- package/dist/gates.js +92 -0
- package/dist/handoff.js +521 -0
- package/dist/hosts.js +331 -0
- package/dist/index.js +1910 -0
- package/dist/init.js +231 -0
- package/dist/leases.js +508 -0
- package/dist/ledger.js +138 -0
- package/dist/manifest.js +255 -0
- package/dist/master.js +453 -0
- package/dist/memoria.js +773 -0
- package/dist/modos.js +220 -0
- package/dist/orkmind.js +487 -0
- package/dist/orquestracao.js +559 -0
- package/dist/phase.js +572 -0
- package/dist/policies.js +176 -0
- package/dist/prompts.js +406 -0
- package/dist/ratelimit.js +179 -0
- package/dist/recall.js +252 -0
- package/dist/retry.js +917 -0
- package/dist/sandbox.js +94 -0
- package/dist/sessoes.js +104 -0
- package/dist/ship.js +551 -0
- package/dist/slug.js +100 -0
- package/dist/superficie.js +1347 -0
- package/dist/thread.js +333 -0
- package/dist/tokens.js +228 -0
- package/dist/types.js +20 -0
- package/dist/util.js +156 -0
- package/dist/verify.js +262 -0
- package/dist/worktree.js +576 -0
- package/dist/yaml.js +112 -0
- package/eval/README.md +85 -0
- package/eval/casos/check-quality.json +103 -0
- package/eval/casos/code-reviewer.json +102 -0
- package/eval/casos/decision-triage.json +103 -0
- package/eval/casos/go-implementation.json +103 -0
- package/eval/casos/goal-definition.json +103 -0
- package/eval/casos/master-metrics.json +103 -0
- package/eval/casos/narrative-guardian.json +163 -0
- package/eval/casos/orkastery-bootstrap.json +102 -0
- package/eval/casos/plan-specification.json +102 -0
- package/eval/casos/roadmap-keeper.json +103 -0
- package/eval/casos/scope-check-capability-map.json +83 -0
- package/eval/casos/security-auditor.json +102 -0
- package/eval/casos/ship-release.json +122 -0
- package/eval/casos/test-engineer.json +122 -0
- package/eval/casos/thread-state.json +82 -0
- package/eval/casos/thread-tracing.json +83 -0
- package/eval/casos/web-performance-auditor.json +102 -0
- package/eval/fixtures/b0-slug-e-modos/caso.json +56 -0
- package/eval/fixtures/b2-master-log/caso.json +91 -0
- package/eval/fixtures/fx-concurrency/caso.json +15 -0
- package/eval/fixtures/fx-hallucination/caso.json +12 -0
- package/eval/fixtures/fx-happy/caso.json +16 -0
- package/eval/fixtures/fx-schema-drift/caso.json +15 -0
- package/eval/fixtures/fx-stale-base/caso.json +12 -0
- package/eval/fixtures/fx-wiki-destroy/caso.json +16 -0
- package/eval/fixtures/superficie-de-rede/README.md +22 -0
- package/eval/fixtures/superficie-de-rede/api-express.js +30 -0
- package/eval/fixtures/superficie-de-rede/api_fastapi.py +17 -0
- package/eval/fixtures/superficie-de-rede/api_gin.go +18 -0
- package/package.json +56 -0
- package/references/README.md +25 -0
- package/references/code-review-axes.md +82 -0
- package/references/definition-of-done.md +74 -0
- package/references/performance-checklist.md +53 -0
- package/references/security-checklist.md +101 -0
- package/references/testing-patterns.md +51 -0
- package/skills/README.md +43 -0
- package/skills/core/orkastery-bootstrap/SKILL.md +89 -0
- package/skills/core/thread-state/SKILL.md +86 -0
- package/skills/governance/decision-triage/SKILL.md +65 -0
- package/skills/governance/narrative-guardian/SKILL.md +66 -0
- package/skills/governance/roadmap-keeper/SKILL.md +60 -0
- package/skills/governance/scope-check-capability-map/SKILL.md +61 -0
- package/skills/observability/thread-tracing/SKILL.md +63 -0
- package/skills/phases/check-quality/SKILL.md +87 -0
- package/skills/phases/go-implementation/SKILL.md +70 -0
- package/skills/phases/goal-definition/SKILL.md +69 -0
- package/skills/phases/master-metrics/SKILL.md +65 -0
- package/skills/phases/plan-specification/SKILL.md +67 -0
- package/skills/phases/ship-release/SKILL.md +68 -0
- package/skills/reviewers/code-reviewer/SKILL.md +61 -0
- package/skills/reviewers/security-auditor/SKILL.md +68 -0
- package/skills/reviewers/test-engineer/SKILL.md +66 -0
- package/skills/reviewers/web-performance-auditor/SKILL.md +63 -0
package/dist/divida.js
ADDED
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* O board de divida: onde os achados e as propostas de ajuste ficam (bloco B5).
|
|
4
|
+
*
|
|
5
|
+
* Toda rodada de auditoria termina em PROPOSTA enderecada ao roadmap do produto, nunca em
|
|
6
|
+
* correcao aplicada. O board e o registro append-only dessas propostas: e dele que o
|
|
7
|
+
* `ork thread new --from-finding <id>` tira o achado, e e nele que a RECORRENCIA de uma
|
|
8
|
+
* regra e contada (a regra mecanica herdada do `ork learn`: 2 recorrencias propoem
|
|
9
|
+
* controle, 3 propoem bloqueante).
|
|
10
|
+
*
|
|
11
|
+
* Duas regras estruturais moram aqui:
|
|
12
|
+
* 1. achado sem proposta COMPLETA nao entra (`achado.sem-proposta`), o que torna o bloco
|
|
13
|
+
* obrigatorio de propostas uma garantia do formato de dados e nao um pedido no prompt;
|
|
14
|
+
* 2. o achado carrega uma CLAIM montada pela MESMA regra das claims de fase
|
|
15
|
+
* (`montarClaim`), porque o auditor nao tem direito a self-report.
|
|
16
|
+
*/
|
|
17
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
18
|
+
if (k2 === undefined) k2 = k;
|
|
19
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
20
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
21
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
22
|
+
}
|
|
23
|
+
Object.defineProperty(o, k2, desc);
|
|
24
|
+
}) : (function(o, m, k, k2) {
|
|
25
|
+
if (k2 === undefined) k2 = k;
|
|
26
|
+
o[k2] = m[k];
|
|
27
|
+
}));
|
|
28
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
29
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
30
|
+
}) : function(o, v) {
|
|
31
|
+
o["default"] = v;
|
|
32
|
+
});
|
|
33
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
34
|
+
var ownKeys = function(o) {
|
|
35
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
36
|
+
var ar = [];
|
|
37
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
38
|
+
return ar;
|
|
39
|
+
};
|
|
40
|
+
return ownKeys(o);
|
|
41
|
+
};
|
|
42
|
+
return function (mod) {
|
|
43
|
+
if (mod && mod.__esModule) return mod;
|
|
44
|
+
var result = {};
|
|
45
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
46
|
+
__setModuleDefault(result, mod);
|
|
47
|
+
return result;
|
|
48
|
+
};
|
|
49
|
+
})();
|
|
50
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
51
|
+
exports.CAMPOS_DA_PROPOSTA = exports.SEVERIDADES = void 0;
|
|
52
|
+
exports.dirDivida = dirDivida;
|
|
53
|
+
exports.caminhoBoard = caminhoBoard;
|
|
54
|
+
exports.lerBoard = lerBoard;
|
|
55
|
+
exports.proximoIdDeAchado = proximoIdDeAchado;
|
|
56
|
+
exports.gravarAchado = gravarAchado;
|
|
57
|
+
exports.exigirAchado = exigirAchado;
|
|
58
|
+
exports.parseSeveridade = parseSeveridade;
|
|
59
|
+
exports.separarArquivoELinha = separarArquivoELinha;
|
|
60
|
+
exports.faltasDaProposta = faltasDaProposta;
|
|
61
|
+
exports.acharDuplicado = acharDuplicado;
|
|
62
|
+
exports.registrarAchado = registrarAchado;
|
|
63
|
+
exports.carimbarAchado = carimbarAchado;
|
|
64
|
+
exports.achadosDaRodada = achadosDaRodada;
|
|
65
|
+
exports.recorrencias = recorrencias;
|
|
66
|
+
exports.abertos = abertos;
|
|
67
|
+
exports.parseEstadoDeAchado = parseEstadoDeAchado;
|
|
68
|
+
exports.tabelaDaDivida = tabelaDaDivida;
|
|
69
|
+
exports.textoDoAchado = textoDoAchado;
|
|
70
|
+
exports.pedidoDoAchado = pedidoDoAchado;
|
|
71
|
+
const path = __importStar(require("node:path"));
|
|
72
|
+
const claims_1 = require("./claims");
|
|
73
|
+
const manifest_1 = require("./manifest");
|
|
74
|
+
const auditoria_1 = require("./auditoria");
|
|
75
|
+
const util_1 = require("./util");
|
|
76
|
+
/** Diretorio do board de divida do projeto. */
|
|
77
|
+
function dirDivida(raiz) {
|
|
78
|
+
return path.join((0, manifest_1.dirEstado)(raiz), 'divida');
|
|
79
|
+
}
|
|
80
|
+
/** O board em si, append-only como o ledger e o `claims.jsonl`. */
|
|
81
|
+
function caminhoBoard(raiz) {
|
|
82
|
+
return path.join(dirDivida(raiz), 'board.jsonl');
|
|
83
|
+
}
|
|
84
|
+
/** Le o board (armazem JSONL: a ultima gravacao de um id vence, como no `claims.jsonl`). */
|
|
85
|
+
function lerBoard(raiz) {
|
|
86
|
+
return (0, util_1.lerJsonl)(caminhoBoard(raiz));
|
|
87
|
+
}
|
|
88
|
+
/** Proximo id sequencial do board (`F1`, `F2`, ...), unico no projeto. */
|
|
89
|
+
function proximoIdDeAchado(achados) {
|
|
90
|
+
return (0, util_1.proximoIdSequencial)(achados.map((a) => a.id), 'F');
|
|
91
|
+
}
|
|
92
|
+
/** Anexa o achado ao board (append-only). */
|
|
93
|
+
function gravarAchado(raiz, achado) {
|
|
94
|
+
(0, util_1.anexarJsonl)(caminhoBoard(raiz), achado);
|
|
95
|
+
}
|
|
96
|
+
/** Acha um achado pelo id, com erro acionavel quando ele nao existe. */
|
|
97
|
+
function exigirAchado(raiz, id) {
|
|
98
|
+
const achado = lerBoard(raiz).find((a) => a.id === id.toUpperCase());
|
|
99
|
+
if (!achado) {
|
|
100
|
+
throw new Error(`achado "${id}" nao existe no board de divida (${caminhoBoard(raiz)}). Veja: ork audit divida`);
|
|
101
|
+
}
|
|
102
|
+
return achado;
|
|
103
|
+
}
|
|
104
|
+
/** Severidades aceitas, para o parse do que vem do auditor. */
|
|
105
|
+
exports.SEVERIDADES = ['critico', 'maior', 'menor'];
|
|
106
|
+
/** Severidade valida, ou null. */
|
|
107
|
+
function parseSeveridade(bruto) {
|
|
108
|
+
const alvo = (bruto ?? '').trim().toLowerCase();
|
|
109
|
+
return exports.SEVERIDADES.includes(alvo) ? alvo : null;
|
|
110
|
+
}
|
|
111
|
+
/** Separa `caminho/arquivo.ts:42` em caminho e linha. Sem linha, a linha e null. */
|
|
112
|
+
function separarArquivoELinha(bruto) {
|
|
113
|
+
const casado = /^(.*):(\d+)$/.exec(bruto.trim());
|
|
114
|
+
if (!casado)
|
|
115
|
+
return { arquivo: bruto.trim(), linha: null };
|
|
116
|
+
return { arquivo: casado[1], linha: Number(casado[2]) };
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Os campos que compoem a proposta de ajuste, com o texto que o auditor le no FORMATO.md.
|
|
120
|
+
*
|
|
121
|
+
* Fonte UNICA: a validacao (`faltasDaProposta`) e a documentacao que a rodada deixa para o
|
|
122
|
+
* auditor saem desta lista. Antes ela existia em codigo e em duas prosas independentes, e
|
|
123
|
+
* foi o proprio pack `reuse` que apontou isso (regra RU4) na rodada ork-reuse-2026-09-03-1.
|
|
124
|
+
*/
|
|
125
|
+
exports.CAMPOS_DA_PROPOSTA = [
|
|
126
|
+
{ campo: 'titulo', rotulo: 'titulo', descricao: 'uma linha, o que esta errado' },
|
|
127
|
+
{
|
|
128
|
+
campo: 'arquivo',
|
|
129
|
+
rotulo: 'arquivo (evidencia caminho:linha)',
|
|
130
|
+
descricao: 'a evidencia principal, no formato caminho/do/arquivo.ext:42',
|
|
131
|
+
},
|
|
132
|
+
{ campo: 'impacto', rotulo: 'impacto', descricao: 'o que quebra ou custa se ficar como esta' },
|
|
133
|
+
{ campo: 'fix', rotulo: 'fix sugerido', descricao: 'o ajuste proposto, em uma frase' },
|
|
134
|
+
{
|
|
135
|
+
campo: 'irreversivel',
|
|
136
|
+
rotulo: 'passo irreversivel (escreva "nenhum" quando o fix for reversivel)',
|
|
137
|
+
descricao: 'o passo irreversivel do fix, ou "nenhum"',
|
|
138
|
+
},
|
|
139
|
+
{ campo: 'estimativa', rotulo: 'estimativa', descricao: '2h | 1d | 1 semana' },
|
|
140
|
+
];
|
|
141
|
+
/**
|
|
142
|
+
* Valida a proposta de ajuste.
|
|
143
|
+
*
|
|
144
|
+
* Os campos abaixo sao o bloco obrigatorio da visao (secao 5.1): evidencia arquivo:linha,
|
|
145
|
+
* impacto, fix sugerido, passo irreversivel e estimativa. Sem eles o achado nao vira insumo
|
|
146
|
+
* de roadmap, e um achado que nao vira insumo de roadmap e relatorio, que e exatamente o
|
|
147
|
+
* que o B5 nao pode produzir.
|
|
148
|
+
*/
|
|
149
|
+
function faltasDaProposta(entrada) {
|
|
150
|
+
const faltas = [];
|
|
151
|
+
for (const { campo, rotulo } of exports.CAMPOS_DA_PROPOSTA) {
|
|
152
|
+
if (String(entrada[campo] ?? '').trim() === '')
|
|
153
|
+
faltas.push(rotulo);
|
|
154
|
+
}
|
|
155
|
+
const pack = auditoria_1.PACKS[entrada.pack];
|
|
156
|
+
if (pack && !pack.regras.some((r) => r.id === entrada.regra)) {
|
|
157
|
+
faltas.push(`regra "${entrada.regra}" fora do pack ${entrada.pack} (validas: ${pack.regras.map((r) => r.id).join(', ')})`);
|
|
158
|
+
}
|
|
159
|
+
return faltas;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* O mesmo achado ja registrado NESTA rodada, se houver.
|
|
163
|
+
*
|
|
164
|
+
* A chave e (rodada, pack, regra, evidencia, titulo). De proposito ela nao atravessa
|
|
165
|
+
* rodadas: o mesmo achado voltando numa rodada POSTERIOR e recorrencia, que e justamente
|
|
166
|
+
* o sinal que promove policy. Duplicata dentro da mesma rodada e outra coisa: e a mesma
|
|
167
|
+
* leitura contada duas vezes, e ela inflaria a recorrencia sem nenhum fato novo.
|
|
168
|
+
*/
|
|
169
|
+
function acharDuplicado(achados, entrada) {
|
|
170
|
+
const { arquivo, linha } = separarArquivoELinha(entrada.arquivo);
|
|
171
|
+
return (achados.find((a) => a.rodada === entrada.rodada &&
|
|
172
|
+
a.pack === entrada.pack &&
|
|
173
|
+
a.regra === entrada.regra &&
|
|
174
|
+
a.arquivo === arquivo &&
|
|
175
|
+
a.linha === linha &&
|
|
176
|
+
a.titulo === (entrada.titulo ?? '').trim()) ?? null);
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Registra um achado no board de divida.
|
|
180
|
+
*
|
|
181
|
+
* Nao grava no ledger: quem registra o evento e a rodada que chamou, porque o ledger da
|
|
182
|
+
* auditoria pertence a rodada e nao ao board.
|
|
183
|
+
*/
|
|
184
|
+
function registrarAchado(raiz, entrada) {
|
|
185
|
+
const faltas = faltasDaProposta(entrada);
|
|
186
|
+
if (faltas.length > 0) {
|
|
187
|
+
throw new Error(`achado.sem-proposta: a proposta de ajuste esta incompleta (faltam: ${faltas.join(', ')}). ` +
|
|
188
|
+
'Cada achado precisa terminar em proposta enderecada ao roadmap do produto.');
|
|
189
|
+
}
|
|
190
|
+
const board = lerBoard(raiz);
|
|
191
|
+
const id = proximoIdDeAchado(board);
|
|
192
|
+
const { arquivo, linha } = separarArquivoELinha(entrada.arquivo);
|
|
193
|
+
const proposta = {
|
|
194
|
+
impacto: entrada.impacto.trim(),
|
|
195
|
+
fix: entrada.fix.trim(),
|
|
196
|
+
estimativa: entrada.estimativa.trim(),
|
|
197
|
+
passoIrreversivel: (entrada.irreversivel ?? '').trim(),
|
|
198
|
+
destino: `roadmap do produto ${entrada.produto}`,
|
|
199
|
+
regime: entrada.memory,
|
|
200
|
+
};
|
|
201
|
+
// A claim do achado nasce pela MESMA regra das claims de fase: o auditor tambem nao
|
|
202
|
+
// pode alegar sem declarar como se comprova.
|
|
203
|
+
const claim = (0, claims_1.montarClaim)({
|
|
204
|
+
id: `${id}.C1`,
|
|
205
|
+
dono: entrada.rodada,
|
|
206
|
+
fase: null,
|
|
207
|
+
arquivo: entrada.arquivo.trim(),
|
|
208
|
+
alegacao: (entrada.alegacao ?? entrada.titulo).trim(),
|
|
209
|
+
verificar: entrada.verificar,
|
|
210
|
+
});
|
|
211
|
+
const achado = {
|
|
212
|
+
id,
|
|
213
|
+
rodada: entrada.rodada,
|
|
214
|
+
pack: entrada.pack,
|
|
215
|
+
regra: entrada.regra,
|
|
216
|
+
severidade: entrada.severidade,
|
|
217
|
+
titulo: entrada.titulo.trim(),
|
|
218
|
+
arquivo,
|
|
219
|
+
linha,
|
|
220
|
+
descricao: entrada.descricao.trim(),
|
|
221
|
+
proposta,
|
|
222
|
+
claim,
|
|
223
|
+
estado: 'aberto',
|
|
224
|
+
produto: entrada.produto,
|
|
225
|
+
estagio: entrada.estagio,
|
|
226
|
+
postura: entrada.postura,
|
|
227
|
+
registradoEm: (0, util_1.agora)(),
|
|
228
|
+
thread: null,
|
|
229
|
+
};
|
|
230
|
+
gravarAchado(raiz, achado);
|
|
231
|
+
return achado;
|
|
232
|
+
}
|
|
233
|
+
/** Regrava o achado com o novo estado (o append mais novo vence). */
|
|
234
|
+
function carimbarAchado(raiz, achado, mudanca) {
|
|
235
|
+
const atualizado = { ...achado, ...mudanca };
|
|
236
|
+
gravarAchado(raiz, atualizado);
|
|
237
|
+
return atualizado;
|
|
238
|
+
}
|
|
239
|
+
/** Achados de uma rodada, na ordem do board. */
|
|
240
|
+
function achadosDaRodada(raiz, rodada) {
|
|
241
|
+
return lerBoard(raiz).filter((a) => a.rodada === rodada);
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Recorrencia por (pack, regra) no board de divida.
|
|
245
|
+
*
|
|
246
|
+
* A regra de promocao e MECANICA e vem da visao (secao 5.2 e onboarding secao 4): 2
|
|
247
|
+
* recorrencias PROPOEM um controle, 3 PROPOEM tornar a policy bloqueante. O `ork` nunca
|
|
248
|
+
* promove sozinho: promover policy e decisao do humano, e o board so mostra a conta.
|
|
249
|
+
*/
|
|
250
|
+
function recorrencias(achados) {
|
|
251
|
+
const grupos = new Map();
|
|
252
|
+
for (const a of achados) {
|
|
253
|
+
// Achado descartado nao procedia e achado resolvido ja foi pago: nenhum dos dois conta
|
|
254
|
+
// como recorrencia, senao a conta que propoe promover policy mediria divida quitada.
|
|
255
|
+
if (a.estado === 'descartado' || a.estado === 'resolvido')
|
|
256
|
+
continue;
|
|
257
|
+
const chave = `${a.pack}::${a.regra}`;
|
|
258
|
+
grupos.set(chave, [...(grupos.get(chave) ?? []), a]);
|
|
259
|
+
}
|
|
260
|
+
const saida = [];
|
|
261
|
+
for (const [chave, lista] of grupos) {
|
|
262
|
+
const [pack, regra] = chave.split('::');
|
|
263
|
+
const n = lista.length;
|
|
264
|
+
const promocao = n >= 3 ? 'bloqueante' : n === 2 ? 'controle' : 'nenhuma';
|
|
265
|
+
saida.push({
|
|
266
|
+
pack: pack,
|
|
267
|
+
regra,
|
|
268
|
+
ocorrencias: n,
|
|
269
|
+
achados: lista.map((a) => a.id),
|
|
270
|
+
promocaoProposta: promocao,
|
|
271
|
+
detalhe: promocao === 'bloqueante'
|
|
272
|
+
? `${n} recorrencias: o board PROPOE promover a policy correspondente para block`
|
|
273
|
+
: promocao === 'controle'
|
|
274
|
+
? `${n} recorrencias: o board PROPOE um controle para a regra ${regra}`
|
|
275
|
+
: 'primeira ocorrencia: sem promocao proposta',
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
return saida.sort((a, b) => b.ocorrencias - a.ocorrencias || a.pack.localeCompare(b.pack) || a.regra.localeCompare(b.regra));
|
|
279
|
+
}
|
|
280
|
+
/** Estados que continuam pendentes de decisao do humano. */
|
|
281
|
+
function abertos(achados) {
|
|
282
|
+
return achados.filter((a) => a.estado === 'aberto');
|
|
283
|
+
}
|
|
284
|
+
/** Estado valido a partir do texto do usuario, ou null. */
|
|
285
|
+
function parseEstadoDeAchado(bruto) {
|
|
286
|
+
const alvo = (bruto ?? '').trim().toLowerCase();
|
|
287
|
+
const validos = ['aberto', 'virou-thread', 'adiado', 'resolvido', 'descartado'];
|
|
288
|
+
return validos.includes(alvo) ? alvo : null;
|
|
289
|
+
}
|
|
290
|
+
/** Tabela de `ork audit divida`. */
|
|
291
|
+
function tabelaDaDivida(raiz, filtro = {}) {
|
|
292
|
+
const board = lerBoard(raiz);
|
|
293
|
+
const alvo = board
|
|
294
|
+
.filter((a) => (filtro.pack ? a.pack === filtro.pack : true))
|
|
295
|
+
.filter((a) => (filtro.todos ? true : a.estado !== 'descartado' && a.estado !== 'resolvido'));
|
|
296
|
+
if (alvo.length === 0) {
|
|
297
|
+
return ('Board de divida vazio' +
|
|
298
|
+
(filtro.pack ? ` para o pack ${filtro.pack}` : '') +
|
|
299
|
+
`.\nRode uma auditoria: ork audit run <pack>`);
|
|
300
|
+
}
|
|
301
|
+
const linhas = alvo.map((a) => [
|
|
302
|
+
a.id,
|
|
303
|
+
a.pack,
|
|
304
|
+
a.regra,
|
|
305
|
+
a.severidade,
|
|
306
|
+
a.claim.estado,
|
|
307
|
+
a.estado,
|
|
308
|
+
a.proposta.estimativa,
|
|
309
|
+
a.proposta.passoIrreversivel.toLowerCase() === 'nenhum' ? 'nao' : 'SIM',
|
|
310
|
+
`${a.arquivo}${a.linha !== null ? ':' + a.linha : ''}`,
|
|
311
|
+
a.titulo,
|
|
312
|
+
]);
|
|
313
|
+
const corpo = (0, util_1.tabela)(['ID', 'PACK', 'REGRA', 'SEVERIDADE', 'CLAIM', 'ESTADO', 'ESTIMATIVA', 'IRREVERSIVEL', 'EVIDENCIA', 'TITULO'], linhas);
|
|
314
|
+
const rec = recorrencias(alvo).filter((r) => r.ocorrencias > 1);
|
|
315
|
+
const linhasRec = rec.length === 0
|
|
316
|
+
? [' (nenhuma regra recorrente ainda)']
|
|
317
|
+
: rec.map((r) => ` ${r.pack} ${r.regra}: ${r.ocorrencias}x -> promocao proposta: ${r.promocaoProposta} (${r.achados.join(', ')})`);
|
|
318
|
+
return [
|
|
319
|
+
`Board de divida do projeto (${caminhoBoard(raiz)})`,
|
|
320
|
+
'',
|
|
321
|
+
corpo,
|
|
322
|
+
'',
|
|
323
|
+
'Recorrencia (2 propoem controle, 3 propoem bloqueante; o `ork` nunca promove sozinho):',
|
|
324
|
+
...linhasRec,
|
|
325
|
+
'',
|
|
326
|
+
`abertos: ${abertos(alvo).length} | total listado: ${alvo.length}`,
|
|
327
|
+
'Achado vira thread com: ork thread new "<nome>" --from-finding <ID>',
|
|
328
|
+
].join('\n');
|
|
329
|
+
}
|
|
330
|
+
/** Detalhe de um achado, usado pelo `--from-finding` e por `ork audit show`. */
|
|
331
|
+
function textoDoAchado(a) {
|
|
332
|
+
const linhas = [];
|
|
333
|
+
linhas.push(` ${a.id} [${a.severidade}] ${a.pack}/${a.regra} ${a.titulo}`);
|
|
334
|
+
linhas.push(` evidencia ${a.arquivo}${a.linha !== null ? ':' + a.linha : ''}`);
|
|
335
|
+
if (a.descricao)
|
|
336
|
+
linhas.push(` descricao ${a.descricao}`);
|
|
337
|
+
linhas.push(` claim ${a.claim.id} [${a.claim.estado}] ${a.claim.alegacao}`);
|
|
338
|
+
linhas.push(` verificar ${a.claim.verificar.join(' && ') || '(sem comando: o auditor nao comprovou o achado)'}`);
|
|
339
|
+
linhas.push(` impacto ${a.proposta.impacto}`);
|
|
340
|
+
linhas.push(` fix ${a.proposta.fix}`);
|
|
341
|
+
linhas.push(` irreversivel ${a.proposta.passoIrreversivel}`);
|
|
342
|
+
linhas.push(` estimativa ${a.proposta.estimativa}`);
|
|
343
|
+
linhas.push(` destino ${a.proposta.destino} (regime ${a.proposta.regime})`);
|
|
344
|
+
linhas.push(` estado ${a.estado}${a.thread ? ` (thread ${a.thread})` : ''}`);
|
|
345
|
+
return linhas.join('\n');
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* O texto do GOAL que um achado entrega a thread aberta por `--from-finding`.
|
|
349
|
+
*
|
|
350
|
+
* E o "sem retrabalho" da visao: o pedido do builder ja nasce com a evidencia, o impacto, o
|
|
351
|
+
* fix proposto, o passo irreversivel e a estimativa que o auditor levantou.
|
|
352
|
+
*/
|
|
353
|
+
function pedidoDoAchado(a) {
|
|
354
|
+
return [
|
|
355
|
+
`Achado de auditoria ${a.id} (pack ${a.pack}, regra ${a.regra}, severidade ${a.severidade}).`,
|
|
356
|
+
'',
|
|
357
|
+
`Titulo: ${a.titulo}`,
|
|
358
|
+
`Evidencia: ${a.arquivo}${a.linha !== null ? ':' + a.linha : ''}`,
|
|
359
|
+
a.descricao ? `Descricao: ${a.descricao}` : '',
|
|
360
|
+
`Alegacao do auditor (claim ${a.claim.id}, estado ${a.claim.estado}): ${a.claim.alegacao}`,
|
|
361
|
+
`Como reexecutar a evidencia: ${a.claim.verificar.join(' && ') || '(nao declarado pelo auditor)'}`,
|
|
362
|
+
'',
|
|
363
|
+
'Proposta de ajuste levantada pela rodada:',
|
|
364
|
+
` impacto ${a.proposta.impacto}`,
|
|
365
|
+
` fix sugerido ${a.proposta.fix}`,
|
|
366
|
+
` irreversivel ${a.proposta.passoIrreversivel}`,
|
|
367
|
+
` estimativa ${a.proposta.estimativa}`,
|
|
368
|
+
'',
|
|
369
|
+
`Origem: rodada ${a.rodada}, produto ${a.produto}, estagio ${a.estagio}, postura ${a.postura}.`,
|
|
370
|
+
'O fix acima e proposta do auditor, nao decisao: valide no GOAL antes de implementar.',
|
|
371
|
+
]
|
|
372
|
+
.filter((l) => l !== '')
|
|
373
|
+
.join('\n');
|
|
374
|
+
}
|
package/dist/doctor.js
ADDED
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* `ork doctor`: o que vale nesta maquina agora.
|
|
4
|
+
*
|
|
5
|
+
* Cada check tem nivel (`ok`, `warn`, `fail`) e, quando falha, a acao de correcao.
|
|
6
|
+
* Qualquer `fail` faz o comando sair com codigo diferente de zero: e um gate, nao um relatorio.
|
|
7
|
+
*/
|
|
8
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
9
|
+
if (k2 === undefined) k2 = k;
|
|
10
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
11
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
12
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
13
|
+
}
|
|
14
|
+
Object.defineProperty(o, k2, desc);
|
|
15
|
+
}) : (function(o, m, k, k2) {
|
|
16
|
+
if (k2 === undefined) k2 = k;
|
|
17
|
+
o[k2] = m[k];
|
|
18
|
+
}));
|
|
19
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
20
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
21
|
+
}) : function(o, v) {
|
|
22
|
+
o["default"] = v;
|
|
23
|
+
});
|
|
24
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
25
|
+
var ownKeys = function(o) {
|
|
26
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
27
|
+
var ar = [];
|
|
28
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
29
|
+
return ar;
|
|
30
|
+
};
|
|
31
|
+
return ownKeys(o);
|
|
32
|
+
};
|
|
33
|
+
return function (mod) {
|
|
34
|
+
if (mod && mod.__esModule) return mod;
|
|
35
|
+
var result = {};
|
|
36
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
37
|
+
__setModuleDefault(result, mod);
|
|
38
|
+
return result;
|
|
39
|
+
};
|
|
40
|
+
})();
|
|
41
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
42
|
+
exports.checar = checar;
|
|
43
|
+
exports.relatorio = relatorio;
|
|
44
|
+
exports.doctor = doctor;
|
|
45
|
+
const fs = __importStar(require("node:fs"));
|
|
46
|
+
const path = __importStar(require("node:path"));
|
|
47
|
+
const adapter = __importStar(require("./adapters/claude-bg"));
|
|
48
|
+
const manifest_1 = require("./manifest");
|
|
49
|
+
const modos_1 = require("./modos");
|
|
50
|
+
const orkmind_1 = require("./orkmind");
|
|
51
|
+
const ratelimit_1 = require("./ratelimit");
|
|
52
|
+
const slug_1 = require("./slug");
|
|
53
|
+
const util_1 = require("./util");
|
|
54
|
+
/**
|
|
55
|
+
* Variaveis que REDIRECIONAM o despacho do `claude` para um provider pago.
|
|
56
|
+
* Presenca delas com politica `subscription-only` e bloqueante (a licao dos US$ 16).
|
|
57
|
+
*/
|
|
58
|
+
const ENVS_QUE_REDIRECIONAM = [
|
|
59
|
+
'ANTHROPIC_API_KEY',
|
|
60
|
+
'ANTHROPIC_AUTH_TOKEN',
|
|
61
|
+
'ANTHROPIC_BASE_URL',
|
|
62
|
+
'CLAUDE_CODE_USE_BEDROCK',
|
|
63
|
+
'CLAUDE_CODE_USE_VERTEX',
|
|
64
|
+
];
|
|
65
|
+
/**
|
|
66
|
+
* Variaveis de provider pago que nao mudam o despacho do `claude`, mas indicam
|
|
67
|
+
* credencial cobrada por token no ambiente. Viram aviso, nao bloqueio.
|
|
68
|
+
*/
|
|
69
|
+
const ENVS_DE_PROVIDER_PAGO = ['OPENROUTER_API_KEY', 'OPENAI_API_KEY', 'GEMINI_API_KEY'];
|
|
70
|
+
function versaoNode() {
|
|
71
|
+
return Number(process.versions.node.split('.')[0]);
|
|
72
|
+
}
|
|
73
|
+
/** Roda todos os checks a partir do diretorio informado. */
|
|
74
|
+
function checar(dirInicial = process.cwd()) {
|
|
75
|
+
const checks = [];
|
|
76
|
+
const major = versaoNode();
|
|
77
|
+
checks.push({
|
|
78
|
+
nome: 'node',
|
|
79
|
+
nivel: major >= 20 ? 'ok' : 'fail',
|
|
80
|
+
detalhe: `v${process.versions.node}`,
|
|
81
|
+
correcao: major >= 20 ? undefined : 'instale Node 20 ou superior',
|
|
82
|
+
});
|
|
83
|
+
const git = (0, util_1.noPath)('git');
|
|
84
|
+
checks.push({
|
|
85
|
+
nome: 'git',
|
|
86
|
+
nivel: git ? 'ok' : 'fail',
|
|
87
|
+
detalhe: git ?? 'nao encontrado no PATH',
|
|
88
|
+
correcao: git ? undefined : 'instale o git',
|
|
89
|
+
});
|
|
90
|
+
const repo = (0, util_1.exec)('git', ['rev-parse', '--is-inside-work-tree'], dirInicial);
|
|
91
|
+
const dentroDeRepo = repo.ok && repo.stdout.trim() === 'true';
|
|
92
|
+
const branch = (0, util_1.exec)('git', ['rev-parse', '--abbrev-ref', 'HEAD'], dirInicial);
|
|
93
|
+
checks.push({
|
|
94
|
+
nome: 'repositorio',
|
|
95
|
+
nivel: dentroDeRepo ? 'ok' : 'fail',
|
|
96
|
+
detalhe: dentroDeRepo ? `branch ${branch.stdout.trim()}` : 'fora de um repositorio git',
|
|
97
|
+
correcao: dentroDeRepo ? undefined : 'rode o ork dentro de um repositorio git',
|
|
98
|
+
});
|
|
99
|
+
const claude = adapter.disponivel();
|
|
100
|
+
const v = claude ? adapter.versao() : null;
|
|
101
|
+
checks.push({
|
|
102
|
+
nome: 'runtime claude-bg',
|
|
103
|
+
nivel: claude ? 'ok' : 'fail',
|
|
104
|
+
detalhe: claude ? `${claude} (${v ?? 'versao desconhecida'})` : 'binario `claude` fora do PATH',
|
|
105
|
+
correcao: claude ? undefined : 'instale o Claude Code e garanta `claude` no PATH',
|
|
106
|
+
});
|
|
107
|
+
const carregado = (0, manifest_1.carregarManifesto)(dirInicial);
|
|
108
|
+
if (!carregado) {
|
|
109
|
+
checks.push({
|
|
110
|
+
nome: 'manifesto',
|
|
111
|
+
nivel: 'fail',
|
|
112
|
+
detalhe: `${manifest_1.NOME_MANIFESTO} nao encontrado a partir de ${dirInicial}`,
|
|
113
|
+
correcao: 'ork init',
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
checks.push({
|
|
118
|
+
nome: 'manifesto',
|
|
119
|
+
nivel: carregado.erros.length > 0 ? 'fail' : carregado.avisos.length > 0 ? 'warn' : 'ok',
|
|
120
|
+
detalhe: carregado.erros.length > 0
|
|
121
|
+
? `${carregado.caminho}: ${carregado.erros.join('; ')}`
|
|
122
|
+
: `${carregado.caminho} (${carregado.bytes} B de ${manifest_1.LIMITE_MANIFESTO_BYTES})` +
|
|
123
|
+
(carregado.avisos.length > 0 ? `; ${carregado.avisos.join('; ')}` : ''),
|
|
124
|
+
correcao: carregado.erros.length > 0 ? 'corrija o manifesto e rode ork doctor de novo' : undefined,
|
|
125
|
+
});
|
|
126
|
+
const abbrev = carregado.manifesto.project.abbrev;
|
|
127
|
+
const checkAbbrev = (0, slug_1.validarAbbrev)(abbrev);
|
|
128
|
+
checks.push({
|
|
129
|
+
nome: 'abbrev do projeto',
|
|
130
|
+
nivel: checkAbbrev.ok ? 'ok' : 'fail',
|
|
131
|
+
detalhe: checkAbbrev.ok
|
|
132
|
+
? `"${abbrev}" (parte 1 do slug de sessao)`
|
|
133
|
+
: checkAbbrev.erro,
|
|
134
|
+
correcao: checkAbbrev.ok ? undefined : 'defina project.abbrev com ate 3 caracteres [a-z0-9]',
|
|
135
|
+
});
|
|
136
|
+
const cond = carregado.manifesto.conduction;
|
|
137
|
+
checks.push({
|
|
138
|
+
nome: 'modos de conducao',
|
|
139
|
+
nivel: 'ok',
|
|
140
|
+
detalhe: `padrao ${modos_1.MODOS[cond.default_mode].tag}; ` +
|
|
141
|
+
`permitidos ${cond.allowed_modes.map((m) => modos_1.MODOS[m].tag).join(', ')}`,
|
|
142
|
+
});
|
|
143
|
+
const politica = carregado.manifesto.runtime.provider_policy;
|
|
144
|
+
const soAssinatura = politica === 'subscription-only';
|
|
145
|
+
const redirecionam = ENVS_QUE_REDIRECIONAM.filter((e) => (process.env[e] ?? '').trim() !== '');
|
|
146
|
+
const pagas = ENVS_DE_PROVIDER_PAGO.filter((e) => (process.env[e] ?? '').trim() !== '');
|
|
147
|
+
const bloqueia = soAssinatura && redirecionam.length > 0;
|
|
148
|
+
checks.push({
|
|
149
|
+
nome: 'custo e provider',
|
|
150
|
+
nivel: bloqueia ? 'fail' : soAssinatura && pagas.length > 0 ? 'warn' : 'ok',
|
|
151
|
+
detalhe: bloqueia
|
|
152
|
+
? `politica ${politica} violada: ${redirecionam.join(', ')} redireciona o despacho do claude`
|
|
153
|
+
: pagas.length > 0
|
|
154
|
+
? `politica ${politica}; despacho segue na assinatura local, mas ha credencial paga no ambiente: ${pagas.join(', ')}`
|
|
155
|
+
: `politica ${politica}; nenhuma variavel de provider pago ativa`,
|
|
156
|
+
correcao: bloqueia
|
|
157
|
+
? `remova do ambiente: ${redirecionam.join(', ')} (o despacho usa a assinatura Claude local)`
|
|
158
|
+
: pagas.length > 0
|
|
159
|
+
? `nenhuma acao obrigatoria; nunca despache fase por ${pagas.join(', ')}`
|
|
160
|
+
: undefined,
|
|
161
|
+
});
|
|
162
|
+
// Bloco B6: o regime de memoria e um CHECK, nao um detalhe do manifesto. Manifesto
|
|
163
|
+
// que pede `orkmind` e nao consegue liga-lo vira `warn` com a correcao, nunca `fail`:
|
|
164
|
+
// memoria semantica melhora o produto, e nao e requisito dele.
|
|
165
|
+
const { estado } = (0, orkmind_1.resolverRegime)(carregado.manifesto, null);
|
|
166
|
+
checks.push({
|
|
167
|
+
nome: 'regime de memoria',
|
|
168
|
+
nivel: estado.efetivo === 'orkmind' ? 'ok' : estado.pedido === 'orkmind' ? 'warn' : 'ok',
|
|
169
|
+
detalhe: estado.pedido === 'orkmind' && estado.efetivo === 'files'
|
|
170
|
+
? `pedido orkmind, efetivo files (${estado.motivo}): ${estado.detalhe}`
|
|
171
|
+
: estado.efetivo === 'orkmind'
|
|
172
|
+
? `orkmind ativo no tenant "${estado.tenant}" pela variavel ${estado.variavel}`
|
|
173
|
+
: 'files (fallback honesto: handoff por arquivos, ponteiro path#ancora)',
|
|
174
|
+
correcao: estado.pedido === 'orkmind' && estado.efetivo === 'files' ? estado.correcao : undefined,
|
|
175
|
+
});
|
|
176
|
+
// Bloco B3: a fila de rate limit e estado do mundo, nao detalhe interno. Uma fase
|
|
177
|
+
// esperando janela e uma fase que NAO esta rodando, e quem olha o doctor precisa
|
|
178
|
+
// saber disso antes de concluir que a thread travou.
|
|
179
|
+
const fila = (0, ratelimit_1.lerFilaDeRetomada)(carregado.raiz);
|
|
180
|
+
const esperando = fila.filter((p) => p.estado === 'aguardando');
|
|
181
|
+
const escalados = fila.filter((p) => p.estado === 'escalado');
|
|
182
|
+
checks.push({
|
|
183
|
+
nome: 'fila de rate limit',
|
|
184
|
+
nivel: escalados.length > 0 ? 'warn' : 'ok',
|
|
185
|
+
detalhe: fila.length === 0
|
|
186
|
+
? 'vazia: nenhuma fase morreu por limite de uso neste projeto'
|
|
187
|
+
: `${esperando.length} aguardando janela, ${escalados.length} escalado(s) para humano` +
|
|
188
|
+
(esperando.length > 0 ? `; proxima janela em ${esperando[0].liberaEm}` : ''),
|
|
189
|
+
correcao: escalados.length > 0
|
|
190
|
+
? `pedido escalado alem do limite de retry.max_tentativas: ork retry list e ork gate approve`
|
|
191
|
+
: esperando.length > 0
|
|
192
|
+
? 'ork retry resume retoma o que ja liberou'
|
|
193
|
+
: undefined,
|
|
194
|
+
});
|
|
195
|
+
const dirEstadoProjeto = path.join(carregado.raiz, manifest_1.DIR_ESTADO);
|
|
196
|
+
const threads = path.join(dirEstadoProjeto, 'threads');
|
|
197
|
+
const qtd = fs.existsSync(threads)
|
|
198
|
+
? fs.readdirSync(threads, { withFileTypes: true }).filter((e) => e.isDirectory()).length
|
|
199
|
+
: 0;
|
|
200
|
+
checks.push({
|
|
201
|
+
nome: 'estado do projeto',
|
|
202
|
+
nivel: fs.existsSync(dirEstadoProjeto) ? 'ok' : 'warn',
|
|
203
|
+
detalhe: fs.existsSync(dirEstadoProjeto)
|
|
204
|
+
? `${dirEstadoProjeto} (${qtd} thread(s))`
|
|
205
|
+
: `${dirEstadoProjeto} ausente`,
|
|
206
|
+
correcao: fs.existsSync(dirEstadoProjeto) ? undefined : 'ork init cria o diretorio de estado',
|
|
207
|
+
});
|
|
208
|
+
if (claude) {
|
|
209
|
+
const sessoes = adapter.listarSessoes(carregado.raiz);
|
|
210
|
+
checks.push({
|
|
211
|
+
nome: 'sessoes vivas do projeto',
|
|
212
|
+
nivel: 'ok',
|
|
213
|
+
detalhe: sessoes.length === 0
|
|
214
|
+
? 'nenhuma sessao ativa sob a raiz do projeto'
|
|
215
|
+
: sessoes.map((s) => `${s.name ?? s.sessionId.slice(0, 8)} (${s.state ?? s.status ?? '?'})`).join(', '),
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
return checks;
|
|
220
|
+
}
|
|
221
|
+
/** Texto do relatorio, com o resumo "o que vale nesta maquina agora". */
|
|
222
|
+
function relatorio(checks) {
|
|
223
|
+
const linhas = [];
|
|
224
|
+
linhas.push('ork doctor: o que vale nesta maquina agora');
|
|
225
|
+
linhas.push('');
|
|
226
|
+
const largura = Math.max(...checks.map((c) => c.nome.length));
|
|
227
|
+
for (const c of checks) {
|
|
228
|
+
linhas.push(` ${(0, util_1.simbolo)(c.nivel)} ${c.nome.padEnd(largura)} ${c.detalhe}`);
|
|
229
|
+
if (c.nivel !== 'ok' && c.correcao) {
|
|
230
|
+
linhas.push(` ${' '.repeat(largura)} correcao: ${c.correcao}`);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
const falhas = checks.filter((c) => c.nivel === 'fail').length;
|
|
234
|
+
const avisos = checks.filter((c) => c.nivel === 'warn').length;
|
|
235
|
+
linhas.push('');
|
|
236
|
+
linhas.push(falhas > 0
|
|
237
|
+
? `Veredito: BLOQUEADO (${falhas} fail, ${avisos} warn). Corrija os itens acima antes de despachar fase.`
|
|
238
|
+
: `Veredito: PRONTO (${avisos} warn). Despacho de fase liberado por \`ork phase run\`.`);
|
|
239
|
+
return linhas.join('\n');
|
|
240
|
+
}
|
|
241
|
+
/** Executa o doctor e devolve o codigo de saida (0 = pronto). */
|
|
242
|
+
function doctor(dirInicial = process.cwd()) {
|
|
243
|
+
const checks = checar(dirInicial);
|
|
244
|
+
const falhas = checks.filter((c) => c.nivel === 'fail').length;
|
|
245
|
+
return { texto: relatorio(checks), codigo: falhas > 0 ? 1 : 0 };
|
|
246
|
+
}
|