Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/cli/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ program.command("scan")
.option("--config <path>", "path to debtlens.config.json")
.option("--cwd <path>", "working directory", process.cwd())
.option("--no-color", "disable ANSI color in terminal output")
.option("-q, --quiet", "print only the summary line, suppress individual findings")
.action(async (target: string, rawOptions: Record<string, unknown>) => {
try {
const format = parseFormat(String(rawOptions.format ?? "terminal"));
Expand Down Expand Up @@ -88,7 +89,7 @@ program.command("scan")
? applyBaseline(result, loadBaseline(cwd, String(rawOptions.baseline)))
: result;

const report = renderReport(reported, format, { color: rawOptions.color !== false && format === "terminal" && process.stdout.isTTY });
const report = renderReport(reported, format, { color: rawOptions.color !== false && format === "terminal" && process.stdout.isTTY, quiet: rawOptions.quiet === true });

if (rawOptions.output) {
const outputPath = resolve(cwd, String(rawOptions.output));
Expand Down
4 changes: 2 additions & 2 deletions src/reporters/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import { renderMarkdown } from "./markdownReporter.js";
import { renderSarif } from "./sarifReporter.js";
import { renderTerminal } from "./terminalReporter.js";

export function renderReport(result: ScanResult, format: OutputFormat, options: { color?: boolean } = {}): string {
export function renderReport(result: ScanResult, format: OutputFormat, options: { color?: boolean; quiet?: boolean } = {}): string {
if (format === "json") return renderJson(result);
if (format === "markdown") return renderMarkdown(result);
if (format === "sarif") return renderSarif(result);
return renderTerminal(result, { color: options.color ?? true });
return renderTerminal(result, { color: options.color ?? true, quiet: options.quiet });
}
6 changes: 5 additions & 1 deletion src/reporters/terminalReporter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,18 @@ import { createColorizer } from "../utils/color.js";

const severityOrder: Severity[] = ["high", "medium", "low", "info"];

export function renderTerminal(result: ScanResult, options: { color: boolean } = { color: true }): string {
export function renderTerminal(result: ScanResult, options: { color: boolean; quiet?: boolean } = { color: true }): string {
const color = createColorizer(options.color);
const lines: string[] = [];

lines.push(color.bold("DebtLens Report"));
lines.push(`Scanned ${result.summary.filesScanned} files with ${result.summary.rulesRun} rules in ${result.summary.elapsedMs}ms.`);
lines.push(`Issues: ${result.summary.totalIssues} | high ${result.summary.bySeverity.high} | medium ${result.summary.bySeverity.medium} | low ${result.summary.bySeverity.low} | info ${result.summary.bySeverity.info}`);

if (options.quiet) {
return `${lines.join("\n")}\n`;
}

if (result.issues.length === 0) {
lines.push("");
lines.push("No maintainability debt found at the configured severity level.");
Expand Down
59 changes: 59 additions & 0 deletions tests/reporters/terminalReporter.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import assert from "node:assert/strict";
import { describe, it } from "node:test";
import type { DebtIssue, ScanResult, Severity } from "../../src/core/types.js";
import { renderTerminal } from "../../src/reporters/terminalReporter.js";

function makeResult(issues: DebtIssue[]): ScanResult {
const bySeverity: Record<Severity, number> = { info: 0, low: 0, medium: 0, high: 0 };
for (const issue of issues) bySeverity[issue.severity] += 1;
return {
issues,
summary: {
totalIssues: issues.length,
bySeverity,
byRule: {},
filesScanned: 3,
rulesRun: 8,
elapsedMs: 42,
},
options: { target: ".", include: [], exclude: [], minSeverity: "info", rules: undefined },
};
}

const issue: DebtIssue = {
id: "dl_test",
ruleId: "prop-drilling",
ruleName: "Prop drilling",
severity: "high",
confidence: 0.9,
message: "Parent forwards 5 props across 2 components.",
file: "src/Parent.tsx",
location: { startLine: 10, endLine: 20 },
tags: [],
};

describe("renderTerminal", () => {
it("includes summary line", () => {
const out = renderTerminal(makeResult([issue]), { color: false });
assert.ok(out.includes("Issues: 1 | high 1"));
});

it("includes individual findings by default", () => {
const out = renderTerminal(makeResult([issue]), { color: false });
assert.ok(out.includes("Prop drilling"));
assert.ok(out.includes("src/Parent.tsx"));
});

it("quiet mode omits individual findings", () => {
const out = renderTerminal(makeResult([issue]), { color: false, quiet: true });
assert.ok(out.includes("Issues: 1 | high 1"), "summary should be present");
assert.ok(!out.includes("src/Parent.tsx"), "file path should not appear in quiet mode");
assert.ok(!out.includes("Prop drilling"), "rule name should not appear in quiet mode");
});

it("quiet mode still shows summary when no issues", () => {
const out = renderTerminal(makeResult([]), { color: false, quiet: true });
assert.ok(out.includes("Issues: 0"));
assert.ok(!out.includes("No maintainability debt found"));
});
});
Loading