Skip to main content
Glama
ascii.ts3.04 kB
import type { Finding, Severity } from '../types.js'; import { colors } from './colors.js'; import { basename } from 'node:path'; type Style = 'ascii' | 'unicode'; type IconSet = 'ascii' | 'emoji'; function icon(sev: Severity, set: IconSet): string { if (set === 'emoji') { if (sev === 'critical') return '⚠️❗'; if (sev === 'medium') return '🔍'; return 'ⓘ'; } // ASCII-only, consistent width icons if (sev === 'critical') return '/!\\'; // hazard if (sev === 'medium') return '(!)'; // caution return '(.)'; // info/low } function bar(done: number, total: number, width = 24, style: Style = 'ascii'): string { const ratio = total === 0 ? 0 : Math.max(0, Math.min(1, done / total)); const filled = Math.round(width * ratio); const empty = width - filled; const pct = total === 0 ? 0 : Math.round(ratio * 100); if (style === 'unicode') { const fillChar = '█'; const emptyChar = '░'; return `[${fillChar.repeat(filled)}${emptyChar.repeat(empty)}] ${done}/${total} (${pct}%)`; } // ASCII dotted bar: '|' + '='*filled + '.'*empty + '| x/y (p%)' return `|${'='.repeat(filled)}${'.'.repeat(empty)}| ${done}/${total} (${pct}%)`; } export interface AsciiOptions { title?: string; style?: Style; // ascii | unicode icons?: IconSet; // ascii | emoji barWidth?: number; showRuleIds?: boolean; // default true } export function renderAscii(findings: Finding[], opts?: AsciiOptions): string { const bySev: Record<Severity, Finding[]> = { critical: [], medium: [], low: [] }; for (const f of findings) bySev[f.severity].push(f); const total = findings.length; const header = [ colors.bold('=== Risk Audit Report ==='), `Overall: ${bar(total, total, opts?.barWidth ?? 24, opts?.style ?? 'ascii')}`, '' ]; const sections: string[] = []; const order: Severity[] = ['critical', 'medium', 'low']; for (const sev of order) { const group = bySev[sev]; const titleText = sev === 'critical' ? 'Critical (fix immediately)' : sev === 'medium' ? 'Medium Priority' : 'Low Priority'; const emojiPrefix = (opts?.icons ?? 'ascii') === 'emoji' ? (sev === 'critical' ? '⚠️ ' : sev === 'medium' ? '🔍 ' : 'ⓘ ') : ''; const coloredTitle = sev === 'critical' ? colors.red(titleText) : sev === 'medium' ? colors.yellow(titleText) : colors.cyan(titleText); sections.push(`${emojiPrefix}${coloredTitle}: ${bar(group.length, total, opts?.barWidth ?? 24, opts?.style ?? 'ascii')}`); group.forEach((f, idx) => { const fileLabel = f.file ? `${basename(f.file)}` : 'offset'; const locRaw = f.file ? `${fileLabel}:${f.range.start.line}-${f.range.end.line}` : `offset`; const loc = colors.green(locRaw); const head = (opts?.showRuleIds ?? false) ? `${f.ruleId} ${f.message}` : `${f.message}`; sections.push(`${idx + 1}. ${head} — ${loc}`); if (f.fix) { sections.push(` fix: ${f.fix}`); } }); sections.push(''); } return [...header, ...sections].join('\n'); }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/rachellarralde/risk-audit-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server