akm-cli
Version:
akm (Agent Knowledge Manager) — a portable, local-first capability library for AI agents. Discover, load, share, and improve reusable skills, scripts, workflows, and knowledge across any shell-capable coding agent, including Claude Code, OpenCode, and Cur
108 lines (107 loc) • 4.44 kB
JavaScript
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
import { INTERESTING_DELTA_PATHS, readNumericPath } from "./windows.js";
function padRight(s, width) {
return s.length >= width ? s : s + " ".repeat(width - s.length);
}
function renderTable(headers, rows) {
const widths = headers.map((h, i) => Math.max(h.length, ...rows.map((r) => (r[i] ?? "").length)));
const lines = [];
lines.push(headers.map((h, i) => padRight(h, widths[i] ?? 0)).join(" "));
for (const row of rows) {
lines.push(row.map((cell, i) => padRight(cell ?? "", widths[i] ?? 0)).join(" "));
}
return lines.join("\n");
}
/**
* Render `--detail per-run` rows as a TSV-ish aligned table. The column
* shape is stable so repeated operator reports remain easy to compare.
*
* Columns: ts | ok | actions | refl_ok/fail/cd/skip |
* distill_q/llm-fail/qrej/cfg/skip | cons_proc/promo/merge/del |
* mem_cons/written/skip | orphans | lint_f/fl
*/
export function renderRunsDetailMd(runs) {
const headers = [
"ts",
"ok",
"strategy",
"actions",
"refl_ok/fail/cd/skip",
"distill_q/llm-fail/judge/validator/cfg/skip",
"cons_proc/promo/merge/del",
"mem_cons/written/skip",
"orphans",
"lint_f/fl",
"result_status",
];
const rows = runs.map((r) => {
const totalActions = r.actions.reflect.ok +
r.actions.reflect.failed +
r.actions.reflect.cooldown +
r.actions.reflect.skipped +
r.actions.distill.queued +
r.actions.distill.llmFailed +
r.actions.distill.judgeRejected +
r.actions.distill.validatorRejected +
r.actions.distill.configDisabled +
r.actions.distill.skipped +
r.actions.memoryPrune +
r.actions.memoryInference +
r.actions.error;
return [
r.startedAt,
String(r.ok),
r.strategy ?? "",
String(totalActions),
`${r.actions.reflect.ok}/${r.actions.reflect.failed}/${r.actions.reflect.cooldown}/${r.actions.reflect.skipped}`,
`${r.actions.distill.queued}/${r.actions.distill.llmFailed}/${r.actions.distill.judgeRejected}/${r.actions.distill.validatorRejected}/${r.actions.distill.configDisabled}/${r.actions.distill.skipped}`,
`${r.consolidation.processed}/${r.consolidation.promoted}/${r.consolidation.merged}/${r.consolidation.deleted}`,
`${r.memoryInference.considered}/${r.memoryInference.written}/${r.memoryInference.skippedNoFacts}`,
String(r.orphansPurged),
`${r.lintFixed}/${r.lintFlagged}`,
r.resultStatus ?? "valid",
];
});
return renderTable(headers, rows);
}
/**
* Render a window-compare comparison as a side-by-side metric table with a
* delta column. Bad-direction deltas (e.g. +pct on failed counts) get a `!`
* marker prefix.
*/
export function renderWindowCompareMd(windows, deltas) {
if (windows.length === 0)
return "";
const headers = ["metric", ...windows.map((w) => w.name), "delta"];
const badIfPositive = new Set([
"improve.actions.reflect.failed",
"improve.actions.distill.llmFailed",
"improve.wallTime.medianMs",
"improve.wallTime.p95Ms",
"improve.memoryInference.skippedNoFacts",
]);
const rows = [];
const paths = [
"runs",
"improve.resultRows.included",
"improve.resultRows.skipped.invalid",
...INTERESTING_DELTA_PATHS,
];
for (const path of paths) {
const values = windows.map((w) => String(readNumericPath(w, path)));
const delta = deltas?.[path];
let deltaStr = "—";
if (delta) {
const pct = delta.pctChange;
const num = typeof pct === "number" ? pct : pct;
const sign = typeof num === "number" && num > 0 ? "+" : "";
const formatted = typeof num === "number" ? `${sign}${num}%` : String(num);
const marker = badIfPositive.has(path) && typeof num === "number" && num > 0 ? "!" : "";
deltaStr = marker + formatted;
}
rows.push([path, ...values, deltaStr]);
}
return renderTable(headers, rows);
}