UNPKG

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

76 lines (75 loc) • 4.1 kB
// 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 { flattenForText } from "../generic-render.js"; import { renderStatusEntries, summarizeCounts } from "./status-list.js"; const STATUS_GLYPH = { fail: "✗", warn: "⚠", unknown: "?", pass: "✓" }; const STATUS_RANK = { fail: 0, warn: 1, unknown: 2, pass: 3 }; /** Worst-first, for both the section summary and (via `STATUS_RANK`) row order. */ const STATUS_ORDER = ["fail", "warn", "unknown", "pass"]; /** One evidence field per line, `key: value` — objects/arrays JSON-compact, matching `formatInfoPlain`'s nested-field convention. */ function evidenceLines(evidence) { if (!evidence) return []; return Object.entries(evidence).map(([key, value]) => `${key}: ${value !== null && typeof value === "object" ? JSON.stringify(value) : String(value)}`); } function checkStatusEntry(check, detail) { const status = check.status ?? "unknown"; const meta = [check.kind, check.confidence].filter((v) => !!v).join(", "); return { severityRank: STATUS_RANK[status] ?? 9, glyph: STATUS_GLYPH[status] ?? "?", headline: `${check.name} [${status}${meta ? `, ${meta}` : ""}] ${check.message ?? ""}`.trimEnd(), // Evidence is diagnostic detail, not the headline signal — gated behind // --detail the same way `formatSearchPlain`/`formatCuratePlain` gate // their own verbose fields (full path/editHint/whyMatched, "why" reasoning). detailLines: detail === "brief" ? [] : evidenceLines(check.evidence), }; } /** Render one `hardChecks`/`advisories` section: a count summary header, then worst-first rows. */ function renderCheckSection(title, checks, detail) { if (checks.length === 0) return [`${title}: (none)`]; const counts = {}; for (const check of checks) { const status = check.status ?? "unknown"; counts[status] = (counts[status] ?? 0) + 1; } const summary = summarizeCounts(counts, STATUS_ORDER); const header = `${title} (${checks.length}${summary ? ` — ${summary}` : ""})`; return [header, ...renderStatusEntries(checks.map((check) => checkStatusEntry(check, detail)))]; } /** Fields already rendered explicitly above — everything else in `r` falls to the `flattenForText` sweep below. */ const HANDLED_KEYS = new Set(["ok", "status", "since", "hardChecks", "advisories", "shape", "schemaVersion"]); export function formatHealthPlain(r, detail) { if (r === null || typeof r !== "object") return null; const lines = []; if (typeof r.ok === "boolean") lines.push(`ok: ${r.ok}`); if (typeof r.status === "string") lines.push(`status: ${r.status}`); if (typeof r.since === "string") lines.push(`since: ${r.since}`); const hardChecks = Array.isArray(r.hardChecks) ? r.hardChecks : []; const advisories = Array.isArray(r.advisories) ? r.advisories : []; lines.push("", ...renderCheckSection("hardChecks", hardChecks, detail)); lines.push("", ...renderCheckSection("advisories", advisories, detail)); if (detail === "brief" && [...hardChecks, ...advisories].some((c) => c.evidence && Object.keys(c.evidence).length > 0)) { lines.push("", "(evidence omitted at --detail brief; re-run with --detail normal or --detail full to see it)"); } // Everything else (metrics, improve, and the optional runs/windows/deltas/ // report datasets) is a nested scalar tree, not the array-of-records shape // this formatter exists to fix — keep the existing dotted-path convention // for it rather than inventing a second one. for (const [key, value] of Object.entries(r)) { if (HANDLED_KEYS.has(key) || value === undefined) continue; const fieldLines = []; flattenForText(value, key, fieldLines); if (fieldLines.length > 0) lines.push("", ...fieldLines); } return lines.join("\n").trim(); }