@vibecheckai/cli 3.5.5 → 3.6.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/README.md +1 -1
- package/bin/runners/lib/approve-output.js +235 -0
- package/bin/runners/lib/classify-output.js +204 -0
- package/bin/runners/lib/doctor-output.js +226 -0
- package/bin/runners/lib/fix-output.js +228 -0
- package/bin/runners/lib/next-action.js +5 -5
- package/bin/runners/lib/prove-output.js +220 -0
- package/bin/runners/lib/reality-output.js +231 -0
- package/bin/runners/lib/report-output.js +299 -120
- package/bin/runners/lib/scan-output.js +316 -271
- package/bin/runners/lib/ship-output.js +283 -93
- package/bin/runners/lib/status-output.js +258 -171
- package/bin/runners/runApprove.js +3 -1
- package/bin/runners/runClassify.js +3 -1
- package/bin/runners/runDoctor.js +27 -36
- package/bin/runners/runFix.js +45 -22
- package/bin/runners/runProve.js +18 -58
- package/bin/runners/runReality.js +26 -40
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -89,7 +89,7 @@ vibecheck reality --url http://localhost:3000
|
|
|
89
89
|
|------|-------|----------|
|
|
90
90
|
| FREE | $0 | init --local, scan, ship (static), doctor, polish, report (HTML/MD), context, guard, checkpoint (basic) |
|
|
91
91
|
| STARTER | $39/mo | + init --connect, scan --autofix, report (SARIF/CSV), mcp, reality (basic) |
|
|
92
|
-
| PRO | $
|
|
92
|
+
| PRO | $49/mo | + prove, fix --apply, checkpoint (hallucination), reality (advanced), ai-test |
|
|
93
93
|
|
|
94
94
|
## What It Catches
|
|
95
95
|
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Enterprise Approve Output - V5 "Mission Control" Format
|
|
3
|
+
* Features:
|
|
4
|
+
* - Dynamic "APPROVE" ASCII Art
|
|
5
|
+
* - Authority Verdict Display
|
|
6
|
+
* - Proof Status
|
|
7
|
+
* - Pixel-perfect 80-char alignment
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const path = require('path');
|
|
11
|
+
|
|
12
|
+
// ANSI Color Helpers
|
|
13
|
+
const ESC = '\x1b';
|
|
14
|
+
const chalk = {
|
|
15
|
+
reset: `${ESC}[0m`,
|
|
16
|
+
bold: `${ESC}[1m`,
|
|
17
|
+
dim: `${ESC}[2m`,
|
|
18
|
+
red: `${ESC}[31m`,
|
|
19
|
+
green: `${ESC}[32m`,
|
|
20
|
+
yellow: `${ESC}[33m`,
|
|
21
|
+
cyan: `${ESC}[36m`,
|
|
22
|
+
magenta: `${ESC}[35m`,
|
|
23
|
+
white: `${ESC}[37m`,
|
|
24
|
+
gray: `${ESC}[90m`,
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
28
|
+
// CONFIGURATION
|
|
29
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
30
|
+
|
|
31
|
+
const WIDTH = 80;
|
|
32
|
+
|
|
33
|
+
const BOX = {
|
|
34
|
+
topLeft: '╔', topRight: '╗', bottomLeft: '╚', bottomRight: '╝',
|
|
35
|
+
horizontal: '═', vertical: '║',
|
|
36
|
+
teeRight: '╠', teeLeft: '╣', teeTop: '╤', teeBottom: '╧',
|
|
37
|
+
cross: '╪',
|
|
38
|
+
lightH: '─', lightV: '│',
|
|
39
|
+
lightTeeLeft: '├', lightTeeRight: '┤', lightCross: '┼'
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
// EXTERNAL HEADER
|
|
43
|
+
const LOGO_VIBECHECK = `
|
|
44
|
+
██╗ ██╗██╗██████╗ ███████╗ ██████╗██╗ ██╗███████╗ ██████╗██╗ ██╗
|
|
45
|
+
██║ ██║██║██╔══██╗██╔════╝██╔════╝██║ ██║██╔════╝██╔════╝██║ ██╔╝
|
|
46
|
+
██║ ██║██║██████╔╝█████╗ ██║ ███████║█████╗ ██║ █████╔╝
|
|
47
|
+
╚██╗ ██╔╝██║██╔══██╗██╔══╝ ██║ ██╔══██║██╔══╝ ██║ ██╔═██╗
|
|
48
|
+
╚████╔╝ ██║██████╔╝███████╗╚██████╗██║ ██║███████╗╚██████╗██║ ██╗
|
|
49
|
+
╚═══╝ ╚═╝╚═════╝ ╚══════╝ ╚═════╝╚═╝ ╚═╝╚══════╝ ╚═════╝╚═╝ ╚═╝
|
|
50
|
+
`;
|
|
51
|
+
|
|
52
|
+
// HERO: APPROVE (Green/Yellow)
|
|
53
|
+
const LOGO_APPROVE = `
|
|
54
|
+
█████╗ ██████╗ ██████╗ ██████╗ ██████╗ ██╗ ██╗███████╗
|
|
55
|
+
██╔══██╗██╔══██╗██╔══██╗██╔══██╗██╔═══██╗██║ ██║██╔════╝
|
|
56
|
+
███████║██████╔╝██████╔╝██████╔╝██║ ██║██║ ██║█████╗
|
|
57
|
+
██╔══██║██╔═══╝ ██╔══██╗██╔══██╗██║ ██║╚██╗ ██╔╝██╔══╝
|
|
58
|
+
██║ ██║██║ ██║ ██║██████╔╝╚██████╔╝ ╚████╔╝ ███████╗
|
|
59
|
+
╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝╚═════╝ ╚═════╝ ╚═══╝ ╚══════╝
|
|
60
|
+
`;
|
|
61
|
+
|
|
62
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
63
|
+
// UTILITIES
|
|
64
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
65
|
+
|
|
66
|
+
function stripAnsi(str) {
|
|
67
|
+
return str.replace(/\x1b\[[0-9;]*m/g, '');
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function padCenter(str, width) {
|
|
71
|
+
const visibleLen = stripAnsi(str).length;
|
|
72
|
+
const padding = Math.max(0, width - visibleLen);
|
|
73
|
+
const left = Math.floor(padding / 2);
|
|
74
|
+
const right = padding - left;
|
|
75
|
+
return ' '.repeat(left) + str + ' '.repeat(right);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function padRight(str, len) {
|
|
79
|
+
const visibleLen = stripAnsi(str).length;
|
|
80
|
+
const truncated = visibleLen > len ? str.substring(0, len - 3) + '...' : str;
|
|
81
|
+
return truncated + ' '.repeat(Math.max(0, len - visibleLen));
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
85
|
+
// MAIN FORMATTER
|
|
86
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
87
|
+
|
|
88
|
+
function formatApproveOutput(verdict, options = {}) {
|
|
89
|
+
const {
|
|
90
|
+
action = null,
|
|
91
|
+
authority = 'unknown',
|
|
92
|
+
version = '1.0.0',
|
|
93
|
+
riskLevel = 'UNKNOWN',
|
|
94
|
+
confidence = 0,
|
|
95
|
+
exitCode = 0,
|
|
96
|
+
hardStopsTriggered = [],
|
|
97
|
+
proofs = {},
|
|
98
|
+
analysis = null,
|
|
99
|
+
notes = '',
|
|
100
|
+
timestamp = new Date().toISOString(),
|
|
101
|
+
} = verdict;
|
|
102
|
+
|
|
103
|
+
const actionColor = action === 'PROCEED' ? chalk.green : action === 'DEFER' ? chalk.yellow : chalk.red;
|
|
104
|
+
const actionIcon = action === 'PROCEED' ? '✓' : action === 'DEFER' ? '⚠' : '✗';
|
|
105
|
+
const themeColor = actionColor;
|
|
106
|
+
const statusText = action ? `${action} VERDICT` : 'PENDING';
|
|
107
|
+
|
|
108
|
+
const lines = [];
|
|
109
|
+
|
|
110
|
+
// 1. Render External Header
|
|
111
|
+
lines.push(chalk.cyan + LOGO_VIBECHECK.trim() + chalk.reset);
|
|
112
|
+
lines.push('');
|
|
113
|
+
|
|
114
|
+
// 2. Render Box Top
|
|
115
|
+
lines.push(`${chalk.gray}${BOX.topLeft}${BOX.horizontal.repeat(WIDTH - 2)}${BOX.topRight}${chalk.reset}`);
|
|
116
|
+
lines.push(`${chalk.gray}${BOX.vertical}${' '.repeat(WIDTH - 2)}${BOX.vertical}${chalk.reset}`);
|
|
117
|
+
|
|
118
|
+
// 3. Render Hero Logo (APPROVE)
|
|
119
|
+
LOGO_APPROVE.trim().split('\n').filter(l => l.trim().length > 0).forEach(l => {
|
|
120
|
+
lines.push(`${chalk.gray}${BOX.vertical}${chalk.reset}${themeColor}${padCenter(l.trim(), WIDTH - 2)}${chalk.reset}${chalk.gray}${BOX.vertical}${chalk.reset}`);
|
|
121
|
+
});
|
|
122
|
+
lines.push(`${chalk.gray}${BOX.vertical}${' '.repeat(WIDTH - 2)}${BOX.vertical}${chalk.reset}`);
|
|
123
|
+
|
|
124
|
+
// 4. Info Row
|
|
125
|
+
const ver = options.version || 'v3.5.5';
|
|
126
|
+
const infoText = `${chalk.bold}${ver} APPROVE${chalk.reset} :: VERDICT: ${actionColor}${actionIcon} ${action || 'PENDING'}${chalk.reset}`;
|
|
127
|
+
const metaText = `Authority: ${authority} v${version}`;
|
|
128
|
+
const infoLine = ` ${infoText}${' '.repeat(Math.max(1, WIDTH - 6 - stripAnsi(infoText).length - stripAnsi(metaText).length))}${chalk.dim}${metaText}${chalk.reset}`;
|
|
129
|
+
lines.push(`${chalk.gray}${BOX.vertical}${chalk.reset}${infoLine} ${chalk.gray}${BOX.vertical}${chalk.reset}`);
|
|
130
|
+
|
|
131
|
+
const projectLine = ` Project: ${padRight(path.basename(options.projectPath || process.cwd()), 50)}`;
|
|
132
|
+
lines.push(`${chalk.gray}${BOX.vertical}${chalk.reset}${projectLine}${' '.repeat(WIDTH - 2 - stripAnsi(projectLine).length)}${chalk.gray}${BOX.vertical}${chalk.reset}`);
|
|
133
|
+
|
|
134
|
+
// 5. Split Pane
|
|
135
|
+
lines.push(`${chalk.gray}${BOX.teeRight}${BOX.horizontal.repeat(44)}${BOX.teeTop}${BOX.horizontal.repeat(WIDTH - 47)}${BOX.teeLeft}${chalk.reset}`);
|
|
136
|
+
|
|
137
|
+
function printSplitRow(left, right) {
|
|
138
|
+
const leftContent = padRight(left || '', 42);
|
|
139
|
+
const rightContent = padRight(right || '', WIDTH - 48);
|
|
140
|
+
lines.push(`${chalk.gray}${BOX.vertical}${chalk.reset} ${leftContent} ${chalk.gray}${BOX.lightV}${chalk.reset} ${rightContent} ${chalk.gray}${BOX.vertical}${chalk.reset}`);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Row 1: Headers
|
|
144
|
+
printSplitRow(`${chalk.bold}AUTHORITY VERDICT${chalk.reset}`, `${chalk.bold}PROOFS & ANALYSIS${chalk.reset}`);
|
|
145
|
+
|
|
146
|
+
// Row 2: Divider
|
|
147
|
+
lines.push(`${chalk.gray}${BOX.vertical}${chalk.reset} ${BOX.lightH.repeat(42)} ${chalk.gray}${BOX.lightCross}${chalk.reset} ${BOX.lightH.repeat(WIDTH - 48)} ${chalk.gray}${BOX.vertical}${chalk.reset}`);
|
|
148
|
+
|
|
149
|
+
// Prepare Left Column
|
|
150
|
+
const confidencePercent = Math.round(confidence * 100);
|
|
151
|
+
const leftCol = [
|
|
152
|
+
'',
|
|
153
|
+
` VERDICT [${actionColor}${actionIcon} ${action || 'N/A'}${chalk.reset}]`,
|
|
154
|
+
` Action: ${actionColor}${action || 'N/A'}${chalk.reset}`,
|
|
155
|
+
` Risk Level: ${riskLevel}`,
|
|
156
|
+
` Confidence: ${chalk.cyan}${confidencePercent}%${chalk.reset}`,
|
|
157
|
+
` Exit Code: ${exitCode}`,
|
|
158
|
+
'',
|
|
159
|
+
`${chalk.gray}${BOX.lightH.repeat(42)}${chalk.reset}`,
|
|
160
|
+
`${chalk.bold} HARD STOPS${chalk.reset}`,
|
|
161
|
+
'',
|
|
162
|
+
];
|
|
163
|
+
|
|
164
|
+
if (hardStopsTriggered.length > 0) {
|
|
165
|
+
hardStopsTriggered.slice(0, 3).forEach((stop, i) => {
|
|
166
|
+
const msg = stop.substring(0, 30);
|
|
167
|
+
leftCol.push(` ${chalk.red}✗${chalk.reset} ${msg}`);
|
|
168
|
+
});
|
|
169
|
+
if (hardStopsTriggered.length > 3) {
|
|
170
|
+
leftCol.push(` ${chalk.dim}... ${hardStopsTriggered.length - 3} more${chalk.reset}`);
|
|
171
|
+
}
|
|
172
|
+
} else {
|
|
173
|
+
leftCol.push(` ${chalk.green}✓${chalk.reset} None`);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Prepare Right Column
|
|
177
|
+
const rightCol = [];
|
|
178
|
+
rightCol.push('');
|
|
179
|
+
|
|
180
|
+
// Proofs
|
|
181
|
+
const proofEntries = Object.entries(proofs || {});
|
|
182
|
+
if (proofEntries.length > 0) {
|
|
183
|
+
rightCol.push(`${chalk.bold} PROOFS${chalk.reset}`);
|
|
184
|
+
proofEntries.slice(0, 4).forEach(([key, value]) => {
|
|
185
|
+
const icon = value.startsWith('PASSED') ? chalk.green + '✓' : value.startsWith('FAILED') ? chalk.red + '✗' : chalk.yellow + '○';
|
|
186
|
+
const name = key.substring(0, 20);
|
|
187
|
+
rightCol.push(` ${icon}${chalk.reset} ${name}`);
|
|
188
|
+
});
|
|
189
|
+
if (proofEntries.length > 4) {
|
|
190
|
+
rightCol.push(` ${chalk.dim}... ${proofEntries.length - 4} more${chalk.reset}`);
|
|
191
|
+
}
|
|
192
|
+
rightCol.push('');
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Analysis summary
|
|
196
|
+
if (analysis && analysis.summary) {
|
|
197
|
+
const { summary } = analysis;
|
|
198
|
+
rightCol.push(`${chalk.bold} ANALYSIS${chalk.reset}`);
|
|
199
|
+
rightCol.push(` Safe: ${chalk.green}${summary.safeCount || 0}${chalk.reset}`);
|
|
200
|
+
rightCol.push(` Review: ${chalk.yellow}${summary.reviewCount || 0}${chalk.reset}`);
|
|
201
|
+
rightCol.push(` Blocked: ${chalk.red}${summary.blockedCount || 0}${chalk.reset}`);
|
|
202
|
+
if (summary.totalLinesSavings) {
|
|
203
|
+
rightCol.push(` Lines Saved: ${chalk.cyan}${summary.totalLinesSavings}${chalk.reset}`);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Merge Columns
|
|
208
|
+
const maxRows = Math.max(leftCol.length, rightCol.length);
|
|
209
|
+
for (let i = 0; i < maxRows; i++) {
|
|
210
|
+
printSplitRow(leftCol[i] || '', rightCol[i] || '');
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// 6. Action Footer
|
|
214
|
+
lines.push(`${chalk.gray}${BOX.teeRight}${BOX.horizontal.repeat(WIDTH - 2)}${BOX.teeLeft}${chalk.reset}`);
|
|
215
|
+
lines.push(`${chalk.gray}${BOX.vertical}${chalk.reset} ${chalk.bold}AUTHORITY CONTROL${chalk.reset}${' '.repeat(WIDTH - 19)}${chalk.gray}${BOX.vertical}${chalk.reset}`);
|
|
216
|
+
lines.push(`${chalk.gray}${BOX.vertical}${chalk.reset} ${BOX.lightH.repeat(WIDTH - 4)} ${chalk.gray}${BOX.vertical}${chalk.reset}`);
|
|
217
|
+
|
|
218
|
+
if (action === 'PROCEED') {
|
|
219
|
+
lines.push(`${chalk.gray}${BOX.vertical}${chalk.reset} ${chalk.green}[✓] AUTHORITY APPROVED. PROCEED WITH CAUTION.${chalk.reset}${' '.repeat(WIDTH - 48)}${chalk.gray}${BOX.vertical}${chalk.reset}`);
|
|
220
|
+
} else if (action === 'STOP') {
|
|
221
|
+
lines.push(`${chalk.gray}${BOX.vertical}${chalk.reset} ${chalk.red}[!] AUTHORITY BLOCKED. DEPLOYMENT HALTED.${chalk.reset}${' '.repeat(WIDTH - 45)}${chalk.gray}${BOX.vertical}${chalk.reset}`);
|
|
222
|
+
} else if (action === 'DEFER') {
|
|
223
|
+
lines.push(`${chalk.gray}${BOX.vertical}${chalk.reset} ${chalk.yellow}[⚠] AUTHORITY DEFERRED. REVIEW REQUIRED.${chalk.reset}${' '.repeat(WIDTH - 46)}${chalk.gray}${BOX.vertical}${chalk.reset}`);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
lines.push(`${chalk.gray}${BOX.vertical}${' '.repeat(WIDTH - 2)}${BOX.vertical}${chalk.reset}`);
|
|
227
|
+
lines.push(`${chalk.gray}${BOX.vertical}${chalk.reset} ${chalk.dim}${timestamp}${chalk.reset}${' '.repeat(WIDTH - 2 - timestamp.length - 2)}${chalk.gray}${BOX.vertical}${chalk.reset}`);
|
|
228
|
+
|
|
229
|
+
// 7. Box Bottom
|
|
230
|
+
lines.push(`${chalk.gray}${BOX.bottomLeft}${BOX.horizontal.repeat(WIDTH - 2)}${BOX.bottomRight}${chalk.reset}`);
|
|
231
|
+
|
|
232
|
+
return lines.join('\n');
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
module.exports = { formatApproveOutput };
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Enterprise Classify Output - V5 "Mission Control" Format
|
|
3
|
+
* Features:
|
|
4
|
+
* - Dynamic "CLASSIFY" ASCII Art
|
|
5
|
+
* - Inventory Summary
|
|
6
|
+
* - Duplication & Legacy Analysis
|
|
7
|
+
* - Pixel-perfect 80-char alignment
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const path = require('path');
|
|
11
|
+
|
|
12
|
+
// ANSI Color Helpers
|
|
13
|
+
const ESC = '\x1b';
|
|
14
|
+
const chalk = {
|
|
15
|
+
reset: `${ESC}[0m`,
|
|
16
|
+
bold: `${ESC}[1m`,
|
|
17
|
+
dim: `${ESC}[2m`,
|
|
18
|
+
red: `${ESC}[31m`,
|
|
19
|
+
green: `${ESC}[32m`,
|
|
20
|
+
yellow: `${ESC}[33m`,
|
|
21
|
+
cyan: `${ESC}[36m`,
|
|
22
|
+
magenta: `${ESC}[35m`,
|
|
23
|
+
white: `${ESC}[37m`,
|
|
24
|
+
gray: `${ESC}[90m`,
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
28
|
+
// CONFIGURATION
|
|
29
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
30
|
+
|
|
31
|
+
const WIDTH = 80;
|
|
32
|
+
|
|
33
|
+
const BOX = {
|
|
34
|
+
topLeft: '╔', topRight: '╗', bottomLeft: '╚', bottomRight: '╝',
|
|
35
|
+
horizontal: '═', vertical: '║',
|
|
36
|
+
teeRight: '╠', teeLeft: '╣', teeTop: '╤', teeBottom: '╧',
|
|
37
|
+
cross: '╪',
|
|
38
|
+
lightH: '─', lightV: '│',
|
|
39
|
+
lightTeeLeft: '├', lightTeeRight: '┤', lightCross: '┼'
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
// EXTERNAL HEADER
|
|
43
|
+
const LOGO_VIBECHECK = `
|
|
44
|
+
██╗ ██╗██╗██████╗ ███████╗ ██████╗██╗ ██╗███████╗ ██████╗██╗ ██╗
|
|
45
|
+
██║ ██║██║██╔══██╗██╔════╝██╔════╝██║ ██║██╔════╝██╔════╝██║ ██╔╝
|
|
46
|
+
██║ ██║██║██████╔╝█████╗ ██║ ███████║█████╗ ██║ █████╔╝
|
|
47
|
+
╚██╗ ██╔╝██║██╔══██╗██╔══╝ ██║ ██╔══██║██╔══╝ ██║ ██╔═██╗
|
|
48
|
+
╚████╔╝ ██║██████╔╝███████╗╚██████╗██║ ██║███████╗╚██████╗██║ ██╗
|
|
49
|
+
╚═══╝ ╚═╝╚═════╝ ╚══════╝ ╚═════╝╚═╝ ╚═╝╚══════╝ ╚═════╝╚═╝ ╚═╝
|
|
50
|
+
`;
|
|
51
|
+
|
|
52
|
+
// HERO: CLASSIFY (Cyan/Magenta)
|
|
53
|
+
const LOGO_CLASSIFY = `
|
|
54
|
+
██████╗██╗ █████╗ ███████╗███████╗██╗███████╗██╗ ██╗
|
|
55
|
+
██╔════╝██║ ██╔══██╗██╔════╝██╔════╝██║██╔════╝╚██╗ ██╔╝
|
|
56
|
+
██║ ██║ ███████║███████╗███████╗██║█████╗ ╚████╔╝
|
|
57
|
+
██║ ██║ ██╔══██║╚════██║╚════██║██║██╔══╝ ╚██╔╝
|
|
58
|
+
╚██████╗███████╗██║ ██║███████║███████║██║██║ ██║
|
|
59
|
+
╚═════╝╚══════╝╚═╝ ╚═╝╚══════╝╚══════╝╚═╝╚═╝ ╚═╝
|
|
60
|
+
`;
|
|
61
|
+
|
|
62
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
63
|
+
// UTILITIES
|
|
64
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
65
|
+
|
|
66
|
+
function stripAnsi(str) {
|
|
67
|
+
return str.replace(/\x1b\[[0-9;]*m/g, '');
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function padCenter(str, width) {
|
|
71
|
+
const visibleLen = stripAnsi(str).length;
|
|
72
|
+
const padding = Math.max(0, width - visibleLen);
|
|
73
|
+
const left = Math.floor(padding / 2);
|
|
74
|
+
const right = padding - left;
|
|
75
|
+
return ' '.repeat(left) + str + ' '.repeat(right);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function padRight(str, len) {
|
|
79
|
+
const visibleLen = stripAnsi(str).length;
|
|
80
|
+
const truncated = visibleLen > len ? str.substring(0, len - 3) + '...' : str;
|
|
81
|
+
return truncated + ' '.repeat(Math.max(0, len - visibleLen));
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
85
|
+
// MAIN FORMATTER
|
|
86
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
87
|
+
|
|
88
|
+
function formatClassifyOutput(result, options = {}) {
|
|
89
|
+
const {
|
|
90
|
+
duplicationMap = [],
|
|
91
|
+
legacyMap = [],
|
|
92
|
+
riskClassifications = [],
|
|
93
|
+
summary = {},
|
|
94
|
+
} = result;
|
|
95
|
+
|
|
96
|
+
const { totalFiles = 0, duplicatedFiles = 0, legacyFiles = 0, highRiskFiles = 0, totalDuplicateLines = 0, analysisTimeMs = 0 } = summary;
|
|
97
|
+
|
|
98
|
+
const themeColor = chalk.cyan;
|
|
99
|
+
const statusText = 'INVENTORY COMPLETE';
|
|
100
|
+
|
|
101
|
+
const lines = [];
|
|
102
|
+
|
|
103
|
+
// 1. Render External Header
|
|
104
|
+
lines.push(chalk.cyan + LOGO_VIBECHECK.trim() + chalk.reset);
|
|
105
|
+
lines.push('');
|
|
106
|
+
|
|
107
|
+
// 2. Render Box Top
|
|
108
|
+
lines.push(`${chalk.gray}${BOX.topLeft}${BOX.horizontal.repeat(WIDTH - 2)}${BOX.topRight}${chalk.reset}`);
|
|
109
|
+
lines.push(`${chalk.gray}${BOX.vertical}${' '.repeat(WIDTH - 2)}${BOX.vertical}${chalk.reset}`);
|
|
110
|
+
|
|
111
|
+
// 3. Render Hero Logo (CLASSIFY)
|
|
112
|
+
LOGO_CLASSIFY.trim().split('\n').filter(l => l.trim().length > 0).forEach(l => {
|
|
113
|
+
lines.push(`${chalk.gray}${BOX.vertical}${chalk.reset}${themeColor}${padCenter(l.trim(), WIDTH - 2)}${chalk.reset}${chalk.gray}${BOX.vertical}${chalk.reset}`);
|
|
114
|
+
});
|
|
115
|
+
lines.push(`${chalk.gray}${BOX.vertical}${' '.repeat(WIDTH - 2)}${BOX.vertical}${chalk.reset}`);
|
|
116
|
+
|
|
117
|
+
// 4. Info Row
|
|
118
|
+
const version = options.version || 'v3.5.5';
|
|
119
|
+
const infoText = `${chalk.bold}${version} CLASSIFY${chalk.reset} :: STATUS: ${themeColor}${statusText}${chalk.reset}`;
|
|
120
|
+
const metaText = `Files: ${totalFiles} | Time: ${(analysisTimeMs / 1000).toFixed(1)}s`;
|
|
121
|
+
const infoLine = ` ${infoText}${' '.repeat(Math.max(1, WIDTH - 6 - stripAnsi(infoText).length - stripAnsi(metaText).length))}${chalk.dim}${metaText}${chalk.reset}`;
|
|
122
|
+
lines.push(`${chalk.gray}${BOX.vertical}${chalk.reset}${infoLine} ${chalk.gray}${BOX.vertical}${chalk.reset}`);
|
|
123
|
+
|
|
124
|
+
const projectLine = ` Project: ${padRight(path.basename(options.projectPath || process.cwd()), 50)}`;
|
|
125
|
+
lines.push(`${chalk.gray}${BOX.vertical}${chalk.reset}${projectLine}${' '.repeat(WIDTH - 2 - stripAnsi(projectLine).length)}${chalk.gray}${BOX.vertical}${chalk.reset}`);
|
|
126
|
+
|
|
127
|
+
// 5. Split Pane
|
|
128
|
+
lines.push(`${chalk.gray}${BOX.teeRight}${BOX.horizontal.repeat(44)}${BOX.teeTop}${BOX.horizontal.repeat(WIDTH - 47)}${BOX.teeLeft}${chalk.reset}`);
|
|
129
|
+
|
|
130
|
+
function printSplitRow(left, right) {
|
|
131
|
+
const leftContent = padRight(left || '', 42);
|
|
132
|
+
const rightContent = padRight(right || '', WIDTH - 48);
|
|
133
|
+
lines.push(`${chalk.gray}${BOX.vertical}${chalk.reset} ${leftContent} ${chalk.gray}${BOX.lightV}${chalk.reset} ${rightContent} ${chalk.gray}${BOX.vertical}${chalk.reset}`);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Row 1: Headers
|
|
137
|
+
printSplitRow(`${chalk.bold}INVENTORY SUMMARY${chalk.reset}`, `${chalk.bold}RISK ANALYSIS${chalk.reset}`);
|
|
138
|
+
|
|
139
|
+
// Row 2: Divider
|
|
140
|
+
lines.push(`${chalk.gray}${BOX.vertical}${chalk.reset} ${BOX.lightH.repeat(42)} ${chalk.gray}${BOX.lightCross}${chalk.reset} ${BOX.lightH.repeat(WIDTH - 48)} ${chalk.gray}${BOX.vertical}${chalk.reset}`);
|
|
141
|
+
|
|
142
|
+
// Prepare Left Column
|
|
143
|
+
const leftCol = [
|
|
144
|
+
'',
|
|
145
|
+
` FILES ANALYZED [${totalFiles}]`,
|
|
146
|
+
` Total: ${chalk.cyan}${totalFiles}${chalk.reset}`,
|
|
147
|
+
'',
|
|
148
|
+
`${chalk.gray}${BOX.lightH.repeat(42)}${chalk.reset}`,
|
|
149
|
+
`${chalk.bold} DUPLICATIONS${chalk.reset}`,
|
|
150
|
+
'',
|
|
151
|
+
` Groups: ${chalk.yellow}${duplicationMap.length}${chalk.reset}`,
|
|
152
|
+
` Files: ${chalk.yellow}${duplicatedFiles}${chalk.reset}`,
|
|
153
|
+
` Lines: ${chalk.yellow}${totalDuplicateLines}${chalk.reset}`,
|
|
154
|
+
'',
|
|
155
|
+
`${chalk.gray}${BOX.lightH.repeat(42)}${chalk.reset}`,
|
|
156
|
+
`${chalk.bold} LEGACY CODE${chalk.reset}`,
|
|
157
|
+
'',
|
|
158
|
+
` Files: ${chalk.magenta}${legacyFiles}${chalk.reset}`,
|
|
159
|
+
];
|
|
160
|
+
|
|
161
|
+
// Prepare Right Column
|
|
162
|
+
const highRisk = riskClassifications.filter(r => r.level === 'HIGH' || r.level === 'CRITICAL');
|
|
163
|
+
const rightCol = [];
|
|
164
|
+
rightCol.push('');
|
|
165
|
+
|
|
166
|
+
rightCol.push(` HIGH RISK [${highRisk.length}]`);
|
|
167
|
+
rightCol.push(` Critical: ${chalk.red}${highRisk.filter(r => r.level === 'CRITICAL').length}${chalk.reset}`);
|
|
168
|
+
rightCol.push(` High: ${chalk.yellow}${highRisk.filter(r => r.level === 'HIGH').length}${chalk.reset}`);
|
|
169
|
+
rightCol.push('');
|
|
170
|
+
|
|
171
|
+
if (highRisk.length > 0) {
|
|
172
|
+
rightCol.push(`${chalk.bold} TOP RISKS${chalk.reset}`);
|
|
173
|
+
highRisk.slice(0, 3).forEach((risk, i) => {
|
|
174
|
+
const levelIcon = risk.level === 'CRITICAL' ? chalk.red + '🔴' : chalk.yellow + '🟠';
|
|
175
|
+
const name = (risk.file || risk.reason || 'Unknown').substring(0, 20);
|
|
176
|
+
rightCol.push(` ${levelIcon}${chalk.reset} ${name}`);
|
|
177
|
+
});
|
|
178
|
+
if (highRisk.length > 3) {
|
|
179
|
+
rightCol.push(` ${chalk.dim}... ${highRisk.length - 3} more${chalk.reset}`);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Merge Columns
|
|
184
|
+
const maxRows = Math.max(leftCol.length, rightCol.length);
|
|
185
|
+
for (let i = 0; i < maxRows; i++) {
|
|
186
|
+
printSplitRow(leftCol[i] || '', rightCol[i] || '');
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// 6. Action Footer
|
|
190
|
+
lines.push(`${chalk.gray}${BOX.teeRight}${BOX.horizontal.repeat(WIDTH - 2)}${BOX.teeLeft}${chalk.reset}`);
|
|
191
|
+
lines.push(`${chalk.gray}${BOX.vertical}${chalk.reset} ${chalk.bold}AUTHORITY CONTROL${chalk.reset}${' '.repeat(WIDTH - 19)}${chalk.gray}${BOX.vertical}${chalk.reset}`);
|
|
192
|
+
lines.push(`${chalk.gray}${BOX.vertical}${chalk.reset} ${BOX.lightH.repeat(WIDTH - 4)} ${chalk.gray}${BOX.vertical}${chalk.reset}`);
|
|
193
|
+
|
|
194
|
+
lines.push(`${chalk.gray}${BOX.vertical}${chalk.reset} ${chalk.dim}This is a read-only inventory. Use 'vibecheck approve' for verdicts.${chalk.reset}${' '.repeat(WIDTH - 65)}${chalk.gray}${BOX.vertical}${chalk.reset}`);
|
|
195
|
+
lines.push(`${chalk.gray}${BOX.vertical}${' '.repeat(WIDTH - 2)}${BOX.vertical}${chalk.reset}`);
|
|
196
|
+
lines.push(`${chalk.gray}${BOX.vertical}${chalk.reset} > ${chalk.cyan}vibecheck approve${chalk.reset} [VERDICT] Get authority verdicts ${chalk.gray}${BOX.vertical}${chalk.reset}`);
|
|
197
|
+
|
|
198
|
+
// 7. Box Bottom
|
|
199
|
+
lines.push(`${chalk.gray}${BOX.bottomLeft}${BOX.horizontal.repeat(WIDTH - 2)}${BOX.bottomRight}${chalk.reset}`);
|
|
200
|
+
|
|
201
|
+
return lines.join('\n');
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
module.exports = { formatClassifyOutput };
|