/** * Output formatting helpers. * * Two modes: * - JSON mode (--json flag): print raw JSON, nothing else. * - Human mode (default): pretty-print tables / key-values. */ export interface OutputOptions { json: boolean; } /** Print as JSON (compact). Safe for agent parsing. */ export function printJson(value: unknown): void { process.stdout.write(JSON.stringify(value) + '\n'); } /** Print a human-readable table. columns: [{key, header, width?}]; rows: object[]. */ export function printTable( columns: Array<{ key: string; header: string }>, rows: Array> ): void { const headers = columns.map((c) => c.header); const widths = columns.map((c) => Math.max(c.header.length, ...rows.map((r) => String(r[c.key] ?? '').length))); const sep = widths.map((w) => '-'.repeat(w)).join(' '); const fmtRow = (r: Record) => columns.map((c, i) => String(r[c.key] ?? '').padEnd(widths[i])).join(' '); process.stdout.write(headers.map((h, i) => h.padEnd(widths[i])).join(' ') + '\n'); process.stdout.write(sep + '\n'); rows.forEach((r) => process.stdout.write(fmtRow(r) + '\n')); } /** Print key: value pairs vertically. */ export function printKv(pairs: Array<[string, unknown]>): void { const width = Math.max(...pairs.map(([k]) => k.length)); pairs.forEach(([k, v]) => { const val = typeof v === 'object' && v !== null ? JSON.stringify(v) : String(v ?? ''); process.stdout.write(`${k.padEnd(width)} ${val}\n`); }); } /** Truncate a long string to width, appending ellipsis. */ export function truncate(s: unknown, width = 40): string { const str = String(s ?? ''); return str.length > width ? str.slice(0, width - 1) + '…' : str; }