From 7a228fcd40f87a449e262d4b4c942dacc0694ab3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20=C3=81ngel?= Date: Tue, 31 Mar 2026 02:05:40 +0200 Subject: [PATCH] fix(cli): hide info-level lint findings by default, show with --verbose Info findings are noisy for agents and day-to-day use. Only errors and warnings are shown by default. Pass --verbose to include info findings in both CLI and JSON output. Co-Authored-By: Claude Opus 4.6 (1M context) --- packages/cli/src/commands/lint.ts | 37 ++++++++++++++++++++++------ packages/cli/src/utils/lintFormat.ts | 13 ++++++++-- 2 files changed, 40 insertions(+), 10 deletions(-) diff --git a/packages/cli/src/commands/lint.ts b/packages/cli/src/commands/lint.ts index 527e4a00e..3871d0afb 100644 --- a/packages/cli/src/commands/lint.ts +++ b/packages/cli/src/commands/lint.ts @@ -1,15 +1,31 @@ import { defineCommand } from "citty"; import { c } from "../ui/colors.js"; -import { resolveProject } from "../utils/project.js"; -import { lintProject } from "../utils/lintProject.js"; import { formatLintFindings } from "../utils/lintFormat.js"; +import { lintProject } from "../utils/lintProject.js"; +import { resolveProject } from "../utils/project.js"; import { withMeta } from "../utils/updateCheck.js"; export default defineCommand({ - meta: { name: "lint", description: "Validate a composition for common mistakes" }, + meta: { + name: "lint", + description: "Validate a composition for common mistakes", + }, args: { - dir: { type: "positional", description: "Project directory", required: false }, - json: { type: "boolean", description: "Output findings as JSON", default: false }, + dir: { + type: "positional", + description: "Project directory", + required: false, + }, + json: { + type: "boolean", + description: "Output findings as JSON", + default: false, + }, + verbose: { + type: "boolean", + description: "Show info-level findings (hidden by default)", + default: false, + }, }, async run({ args }) { try { @@ -17,12 +33,13 @@ export default defineCommand({ const lintResult = lintProject(project); if (args.json) { + const allFindings = lintResult.results.flatMap((r) => r.result.findings); const combined = { ok: lintResult.totalErrors === 0, errorCount: lintResult.totalErrors, warningCount: lintResult.totalWarnings, infoCount: lintResult.totalInfos, - findings: lintResult.results.flatMap((r) => r.result.findings), + findings: args.verbose ? allFindings : allFindings.filter((f) => f.severity !== "info"), filesScanned: lintResult.results.length, }; console.log(JSON.stringify(withMeta(combined), null, 2)); @@ -32,7 +49,7 @@ export default defineCommand({ const fileCount = lintResult.results.length; const fileLabel = fileCount === 1 ? (lintResult.results[0]?.file ?? "index.html") : `${fileCount} files`; - console.log(`${c.accent("◆")} Linting ${c.accent(project.name + "/" + fileLabel)}`); + console.log(`${c.accent("◆")} Linting ${c.accent(`${project.name}/${fileLabel}`)}`); console.log(); if (lintResult.totalErrors === 0 && lintResult.totalWarnings === 0) { @@ -40,7 +57,11 @@ export default defineCommand({ return; } - const lines = formatLintFindings(lintResult, { showElementId: true, showSummary: true }); + const lines = formatLintFindings(lintResult, { + showElementId: true, + showSummary: true, + verbose: args.verbose, + }); for (const line of lines) console.log(line); process.exit(lintResult.totalErrors > 0 ? 1 : 0); diff --git a/packages/cli/src/utils/lintFormat.ts b/packages/cli/src/utils/lintFormat.ts index 65506ea9d..539c076ed 100644 --- a/packages/cli/src/utils/lintFormat.ts +++ b/packages/cli/src/utils/lintFormat.ts @@ -8,6 +8,8 @@ export interface LintFormatOptions { showSummary?: boolean; /** Group errors before warnings per file (default: false — interleaved) */ errorsFirst?: boolean; + /** Include info-level findings in output (default: false — only errors/warnings) */ + verbose?: boolean; } /** @@ -17,7 +19,12 @@ export function formatLintFindings( { results, totalErrors, totalWarnings, totalInfos }: ProjectLintResult, options: LintFormatOptions = {}, ): string[] { - const { showElementId = true, showSummary = false, errorsFirst = false } = options; + const { + showElementId = true, + showSummary = false, + errorsFirst = false, + verbose = false, + } = options; const lines: string[] = []; const multiFile = results.length > 1; @@ -25,6 +32,7 @@ export function formatLintFindings( if (result.findings.length === 0) continue; const format = (finding: (typeof result.findings)[0]) => { + if (!verbose && finding.severity === "info") return; const prefix = finding.severity === "error" ? c.error("✗") @@ -41,6 +49,7 @@ export function formatLintFindings( if (errorsFirst) { for (const f of result.findings) if (f.severity === "error") format(f); for (const f of result.findings) if (f.severity === "warning") format(f); + if (verbose) for (const f of result.findings) if (f.severity === "info") format(f); } else { for (const f of result.findings) format(f); } @@ -50,7 +59,7 @@ export function formatLintFindings( const icon = totalErrors > 0 ? c.error("◇") : c.success("◇"); lines.push(""); const summaryParts = [`${totalErrors} error(s)`, `${totalWarnings} warning(s)`]; - if (totalInfos > 0) summaryParts.push(`${totalInfos} info(s)`); + if (verbose && totalInfos > 0) summaryParts.push(`${totalInfos} info(s)`); lines.push(`${icon} ${summaryParts.join(", ")}`); }