@skillguard/cli 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +0 -0
- package/dist/lib/format.js +56 -26
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
File without changes
|
package/dist/lib/format.js
CHANGED
|
@@ -4,9 +4,13 @@ const YELLOW = '\u001B[33m';
|
|
|
4
4
|
const RED = '\u001B[31m';
|
|
5
5
|
const CYAN = '\u001B[36m';
|
|
6
6
|
const DIM = '\u001B[2m';
|
|
7
|
+
const BOX_INNER_WIDTH = 41;
|
|
7
8
|
function maybeColor(enabled, color, text) {
|
|
8
9
|
return enabled ? `${color}${text}${RESET}` : text;
|
|
9
10
|
}
|
|
11
|
+
function stripAnsi(value) {
|
|
12
|
+
return value.replace(/\u001B\[[0-9;]*m/g, '');
|
|
13
|
+
}
|
|
10
14
|
export function shouldUseColor(noColorFlag) {
|
|
11
15
|
if (noColorFlag) {
|
|
12
16
|
return false;
|
|
@@ -30,40 +34,66 @@ function statusLabel(status, color) {
|
|
|
30
34
|
? maybeColor(color, CYAN, 'issued')
|
|
31
35
|
: maybeColor(color, DIM, 'disabled');
|
|
32
36
|
}
|
|
37
|
+
function truncateAscii(value, limit) {
|
|
38
|
+
if (value.length <= limit) {
|
|
39
|
+
return value;
|
|
40
|
+
}
|
|
41
|
+
if (limit <= 3) {
|
|
42
|
+
return '.'.repeat(Math.max(0, limit));
|
|
43
|
+
}
|
|
44
|
+
return `${value.slice(0, limit - 3)}...`;
|
|
45
|
+
}
|
|
46
|
+
function shortenPath(value, limit) {
|
|
47
|
+
if (value.length <= limit) {
|
|
48
|
+
return value;
|
|
49
|
+
}
|
|
50
|
+
if (limit <= 3) {
|
|
51
|
+
return '.'.repeat(Math.max(0, limit));
|
|
52
|
+
}
|
|
53
|
+
const tail = Math.min(16, Math.max(10, Math.floor(limit * 0.42)));
|
|
54
|
+
const head = Math.max(1, limit - tail - 3);
|
|
55
|
+
return `${value.slice(0, head)}...${value.slice(value.length - tail)}`;
|
|
56
|
+
}
|
|
57
|
+
function formatCell(content, width) {
|
|
58
|
+
const visible = stripAnsi(content);
|
|
59
|
+
if (visible.length >= width) {
|
|
60
|
+
return content;
|
|
61
|
+
}
|
|
62
|
+
return content + ' '.repeat(width - visible.length);
|
|
63
|
+
}
|
|
64
|
+
function boxRow(content) {
|
|
65
|
+
const clipped = truncateAscii(content, BOX_INNER_WIDTH);
|
|
66
|
+
return `│${formatCell(clipped, BOX_INNER_WIDTH)}│`;
|
|
67
|
+
}
|
|
68
|
+
function metricRow(label, value) {
|
|
69
|
+
const labelCell = `${label.padEnd(11, ' ')}`;
|
|
70
|
+
return boxRow(` ${labelCell}${value}`);
|
|
71
|
+
}
|
|
33
72
|
export function renderSingleScan(result, color) {
|
|
73
|
+
const fileDisplay = shortenPath(result.file, 28);
|
|
74
|
+
const scoreDisplay = scoreLabel(result.score, color);
|
|
75
|
+
const findingsDisplay = String(result.findings.length);
|
|
76
|
+
const signatureDisplay = statusLabel(result.signatureStatus, color);
|
|
34
77
|
const lines = [
|
|
35
|
-
'
|
|
36
|
-
'
|
|
37
|
-
'
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
78
|
+
`┌${'─'.repeat(BOX_INNER_WIDTH)}┐`,
|
|
79
|
+
boxRow(' SkillGuard Scan Report'),
|
|
80
|
+
`├${'─'.repeat(BOX_INNER_WIDTH)}┤`,
|
|
81
|
+
metricRow('File:', fileDisplay),
|
|
82
|
+
metricRow('Score:', scoreDisplay),
|
|
83
|
+
metricRow('Findings:', findingsDisplay),
|
|
84
|
+
metricRow('Signature:', signatureDisplay),
|
|
42
85
|
];
|
|
43
86
|
if (result.findings.length > 0) {
|
|
44
|
-
lines.push('
|
|
87
|
+
lines.push(`├${'─'.repeat(BOX_INNER_WIDTH)}┤`);
|
|
45
88
|
for (const finding of result.findings.slice(0, 3)) {
|
|
46
89
|
const sev = (finding.severity || 'info').toUpperCase();
|
|
47
90
|
const title = finding.title || finding.description || 'Finding';
|
|
48
|
-
lines.push(
|
|
91
|
+
lines.push(boxRow(` [${sev}] ${truncateAscii(title, 29)}`));
|
|
49
92
|
}
|
|
50
93
|
}
|
|
51
|
-
lines.push('
|
|
94
|
+
lines.push(`└${'─'.repeat(BOX_INNER_WIDTH)}┘`);
|
|
52
95
|
return lines.join('\n');
|
|
53
96
|
}
|
|
54
|
-
function truncate(value, limit) {
|
|
55
|
-
if (value.length <= limit) {
|
|
56
|
-
return value;
|
|
57
|
-
}
|
|
58
|
-
return `${value.slice(0, limit - 1)}…`;
|
|
59
|
-
}
|
|
60
|
-
function padInline(value, target) {
|
|
61
|
-
const strippedLength = value.replace(/\u001B\[[0-9;]*m/g, '').length;
|
|
62
|
-
if (strippedLength >= target) {
|
|
63
|
-
return value;
|
|
64
|
-
}
|
|
65
|
-
return value + ' '.repeat(target - strippedLength);
|
|
66
|
-
}
|
|
67
97
|
export function summarizeScores(results) {
|
|
68
98
|
const summary = {
|
|
69
99
|
total: results.length,
|
|
@@ -80,9 +110,9 @@ export function renderSummary(summary, rootPath, color, failedFiles) {
|
|
|
80
110
|
const lines = [
|
|
81
111
|
`Scanned ${summary.total} skills in ${rootPath}`,
|
|
82
112
|
'',
|
|
83
|
-
` ${maybeColor(color, GREEN, 'safe')}
|
|
84
|
-
` ${maybeColor(color, YELLOW, 'warning')}
|
|
85
|
-
` ${maybeColor(color, RED, 'dangerous')}
|
|
113
|
+
` ${formatCell(maybeColor(color, GREEN, 'safe:'), 11)}${String(summary.safe)}`,
|
|
114
|
+
` ${formatCell(maybeColor(color, YELLOW, 'warning:'), 11)}${String(summary.warning)}`,
|
|
115
|
+
` ${formatCell(maybeColor(color, RED, 'dangerous:'), 11)}${String(summary.dangerous)}`,
|
|
86
116
|
];
|
|
87
117
|
if (failedFiles.length > 0) {
|
|
88
118
|
lines.push('');
|