feat(cli): show artifacts and top findings
Some checks are pending
Check / Node 22 (push) Waiting to run

This commit is contained in:
Vincent Koc 2026-04-27 12:57:10 -07:00
parent c89fb0b1c8
commit ed80dc2d15
No known key found for this signature in database
3 changed files with 88 additions and 5 deletions

View File

@ -83,7 +83,7 @@ async function runCheck(commandArgs) {
if (json) {
console.log(JSON.stringify(report, null, 2));
} else {
console.log(renderTextSummary(report));
console.log(renderTextSummary(report, { artifacts: paths }));
}
if (report.status !== "pass") {
@ -128,7 +128,7 @@ async function runReport(command, commandArgs) {
if (json) {
console.log(JSON.stringify(report, null, 2));
} else {
console.log(renderTextSummary(report));
console.log(renderTextSummary(report, { artifacts: paths }));
}
if (check && report.status !== "pass") {

View File

@ -260,13 +260,48 @@ export async function writeCompatibilityReport(report, options = {}) {
);
}
export function renderTextSummary(report) {
return [
export function renderTextSummary(report, options = {}) {
const lines = [
`Status: ${report.status.toUpperCase()}`,
`Fixtures: ${report.summary.fixtureCount}`,
`Breakages: ${report.summary.breakageCount}`,
...(typeof report.summary.issueCount === "number" ? [`Issues: ${report.summary.issueCount}`] : []),
`Logs: ${report.summary.logCount}`,
].join("\n");
];
const artifacts = Object.entries(options.artifacts ?? {}).filter(([, filePath]) => Boolean(filePath));
if (artifacts.length > 0) {
lines.push("", "Reports:", ...artifacts.map(([name, filePath]) => `- ${artifactLabel(name)}: ${filePath}`));
}
const findings = topTextFindings(report, options.topFindings ?? 3);
if (findings.length > 0) {
lines.push("", "Top findings:", ...findings.map((finding) => `- ${finding}`));
}
return lines.join("\n");
}
function topTextFindings(report, limit) {
if (report.status === "pass" || limit <= 0) {
return [];
}
return [
...(report.breakages ?? []).map((finding) => formatTextFinding(finding, "breakage")),
...(report.issues ?? [])
.filter((issue) => issue.status === "blocking" || issue.severity === "P0" || issue.severity === "P1")
.map((issue) => formatTextFinding(issue, issue.severity ?? "issue")),
...(report.warnings ?? []).map((finding) => formatTextFinding(finding, "warning")),
].slice(0, limit);
}
function formatTextFinding(finding, fallbackLevel) {
const level = finding.level ?? finding.severity ?? fallbackLevel;
const code = finding.code ? ` ${finding.code}` : "";
const message = finding.message ?? finding.title ?? "see report";
const evidence = Array.isArray(finding.evidence) && finding.evidence.length > 0 ? ` (${finding.evidence[0]})` : "";
return `${String(level).toUpperCase()} ${finding.fixture ?? "unknown"}${code}: ${message}${evidence}`;
}
function artifactLabel(name) {
return String(name).replace(/Path$/u, "");
}
export function renderMarkdownReport(report) {

View File

@ -18,6 +18,7 @@ import {
renderJunitXml,
renderMarkdownReport,
renderMarkdownTable,
renderTextSummary,
writeArtifacts,
writeCiOutputArtifacts,
writeReport,
@ -32,6 +33,53 @@ test("markdown report includes summary and inventory", async () => {
assert.match(markdown, /\| sample-plugin \| high \| native-tool \| before_tool_call \| definePluginEntry, registerTool \| tools \|/);
});
test("text summary includes artifact paths and top blocking findings", () => {
const summary = renderTextSummary(
{
status: "fail",
summary: {
fixtureCount: 1,
breakageCount: 1,
issueCount: 1,
logCount: 0,
},
breakages: [
{
fixture: "weather",
code: "missing-expected-seam",
level: "breakage",
message: "weather: missing expected registration registerTool",
evidence: ["src/index.js:12"],
},
],
warnings: [],
issues: [
{
fixture: "weather",
code: "sdk-export-missing",
severity: "P0",
status: "blocking",
title: "SDK export is missing",
evidence: ["src/index.js:1"],
},
],
},
{
artifacts: {
jsonPath: "reports/plugin-inspector-report.json",
markdownPath: "reports/plugin-inspector-report.md",
},
},
);
assert.match(summary, /Status: FAIL/);
assert.match(summary, /Issues: 1/);
assert.match(summary, /Reports:/);
assert.match(summary, /json: reports\/plugin-inspector-report\.json/);
assert.match(summary, /Top findings:/);
assert.match(summary, /BREAKAGE weather missing-expected-seam/);
});
test("compatibility report renderer supports issue metadata and evidence links", () => {
const report = {
generatedAt: "test",