@vibecheckai/cli 3.5.4 → 3.6.0

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.
@@ -0,0 +1,231 @@
1
+ /**
2
+ * Enterprise Reality Output - V5 "Mission Control" Format
3
+ * Features:
4
+ * - Dynamic "REALITY" ASCII Art
5
+ * - Runtime Verification Status
6
+ * - Browser Test Results
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: REALITY (Green/Blue)
53
+ const LOGO_REALITY = `
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 formatRealityOutput(result, options = {}) {
89
+ const {
90
+ verdict = null,
91
+ tests = [],
92
+ passed = 0,
93
+ failed = 0,
94
+ warnings = 0,
95
+ coverage = 0,
96
+ duration = 0,
97
+ url = null,
98
+ success = false,
99
+ error = null,
100
+ } = result;
101
+
102
+ const isSuccess = success || (verdict === 'SHIP' && failed === 0);
103
+ const themeColor = isSuccess ? chalk.green : error ? chalk.red : chalk.yellow;
104
+ const statusText = isSuccess ? 'VERIFICATION PASSED' : error ? 'VERIFICATION FAILED' : 'VERIFICATION IN PROGRESS';
105
+
106
+ const lines = [];
107
+
108
+ // 1. Render External Header
109
+ lines.push(chalk.cyan + LOGO_VIBECHECK.trim() + chalk.reset);
110
+ lines.push('');
111
+
112
+ // 2. Render Box Top
113
+ lines.push(`${chalk.gray}${BOX.topLeft}${BOX.horizontal.repeat(WIDTH - 2)}${BOX.topRight}${chalk.reset}`);
114
+ lines.push(`${chalk.gray}${BOX.vertical}${' '.repeat(WIDTH - 2)}${BOX.vertical}${chalk.reset}`);
115
+
116
+ // 3. Render Hero Logo (REALITY)
117
+ LOGO_REALITY.trim().split('\n').filter(l => l.trim().length > 0).forEach(l => {
118
+ lines.push(`${chalk.gray}${BOX.vertical}${chalk.reset}${themeColor}${padCenter(l.trim(), WIDTH - 2)}${chalk.reset}${chalk.gray}${BOX.vertical}${chalk.reset}`);
119
+ });
120
+ lines.push(`${chalk.gray}${BOX.vertical}${' '.repeat(WIDTH - 2)}${BOX.vertical}${chalk.reset}`);
121
+
122
+ // 4. Info Row
123
+ const version = options.version || 'v3.5.5';
124
+ const infoText = `${chalk.bold}${version} REALITY${chalk.reset} :: STATUS: ${themeColor}${statusText}${chalk.reset}`;
125
+ const metaText = `Tests: ${tests.length} | Passed: ${passed}`;
126
+ const infoLine = ` ${infoText}${' '.repeat(Math.max(1, WIDTH - 6 - stripAnsi(infoText).length - stripAnsi(metaText).length))}${chalk.dim}${metaText}${chalk.reset}`;
127
+ lines.push(`${chalk.gray}${BOX.vertical}${chalk.reset}${infoLine} ${chalk.gray}${BOX.vertical}${chalk.reset}`);
128
+
129
+ const urlLine = url ? ` URL: ${padRight(url, 50)}` : ` Project: ${padRight(path.basename(options.projectPath || process.cwd()), 50)}`;
130
+ lines.push(`${chalk.gray}${BOX.vertical}${chalk.reset}${urlLine}${' '.repeat(WIDTH - 2 - stripAnsi(urlLine).length)}${chalk.gray}${BOX.vertical}${chalk.reset}`);
131
+
132
+ // 5. Split Pane
133
+ lines.push(`${chalk.gray}${BOX.teeRight}${BOX.horizontal.repeat(44)}${BOX.teeTop}${BOX.horizontal.repeat(WIDTH - 47)}${BOX.teeLeft}${chalk.reset}`);
134
+
135
+ function printSplitRow(left, right) {
136
+ const leftContent = padRight(left || '', 42);
137
+ const rightContent = padRight(right || '', WIDTH - 48);
138
+ lines.push(`${chalk.gray}${BOX.vertical}${chalk.reset} ${leftContent} ${chalk.gray}${BOX.lightV}${chalk.reset} ${rightContent} ${chalk.gray}${BOX.vertical}${chalk.reset}`);
139
+ }
140
+
141
+ // Row 1: Headers
142
+ printSplitRow(`${chalk.bold}RUNTIME TESTS${chalk.reset}`, `${chalk.bold}VERIFICATION RESULTS${chalk.reset}`);
143
+
144
+ // Row 2: Divider
145
+ 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}`);
146
+
147
+ // Prepare Left Column
148
+ const totalTests = tests.length || (passed + failed);
149
+ const passRate = totalTests > 0 ? Math.round((passed / totalTests) * 100) : 0;
150
+ const leftCol = [
151
+ '',
152
+ ` VERDICT [${verdict === 'SHIP' ? chalk.green + 'SHIP' : verdict === 'BLOCK' ? chalk.red + 'BLOCK' : chalk.yellow + 'WARN'}${chalk.reset}]`,
153
+ ` Status: ${verdict || 'N/A'}`,
154
+ '',
155
+ `${chalk.gray}${BOX.lightH.repeat(42)}${chalk.reset}`,
156
+ `${chalk.bold} TEST RESULTS${chalk.reset}`,
157
+ '',
158
+ ` Total: ${chalk.cyan}${totalTests}${chalk.reset}`,
159
+ ` Passed: ${chalk.green}${passed}${chalk.reset}`,
160
+ ` Failed: ${chalk.red}${failed}${chalk.reset}`,
161
+ ` Warnings: ${chalk.yellow}${warnings}${chalk.reset}`,
162
+ ` Pass Rate: ${chalk.cyan}${passRate}%${chalk.reset}`,
163
+ ];
164
+
165
+ // Prepare Right Column
166
+ const rightCol = [];
167
+ rightCol.push('');
168
+
169
+ rightCol.push(` COVERAGE [${coverage}%]`);
170
+ rightCol.push(` Routes: ${chalk.cyan}${coverage}%${chalk.reset}`);
171
+ rightCol.push('');
172
+
173
+ if (isSuccess) {
174
+ rightCol.push(`${chalk.green} [✓] VERIFIED${chalk.reset}`);
175
+ rightCol.push(` All runtime tests`);
176
+ rightCol.push(` passed.`);
177
+ } else if (error) {
178
+ rightCol.push(`${chalk.red} [!] FAILED${chalk.reset}`);
179
+ rightCol.push(` Verification error.`);
180
+ rightCol.push('');
181
+ let msg = error.message || 'Unknown error';
182
+ if (msg.length > 25) msg = msg.substring(0, 25) + '...';
183
+ rightCol.push(` ${chalk.red}${msg}${chalk.reset}`);
184
+ } else {
185
+ rightCol.push(`${chalk.yellow} [○] IN PROGRESS${chalk.reset}`);
186
+ rightCol.push(` Running browser`);
187
+ rightCol.push(` tests...`);
188
+ }
189
+
190
+ if (tests.length > 0) {
191
+ rightCol.push('');
192
+ rightCol.push(`${chalk.bold} TEST SUMMARY${chalk.reset}`);
193
+ tests.slice(0, 3).forEach((test, i) => {
194
+ const status = test.passed ? chalk.green + '✓' : chalk.red + '✖';
195
+ const name = (test.name || test.route || 'Test').substring(0, 20);
196
+ rightCol.push(` ${status}${chalk.reset} ${name}`);
197
+ });
198
+ if (tests.length > 3) {
199
+ rightCol.push(` ${chalk.dim}... ${tests.length - 3} more${chalk.reset}`);
200
+ }
201
+ }
202
+
203
+ // Merge Columns
204
+ const maxRows = Math.max(leftCol.length, rightCol.length);
205
+ for (let i = 0; i < maxRows; i++) {
206
+ printSplitRow(leftCol[i] || '', rightCol[i] || '');
207
+ }
208
+
209
+ // 6. Action Footer
210
+ lines.push(`${chalk.gray}${BOX.teeRight}${BOX.horizontal.repeat(WIDTH - 2)}${BOX.teeLeft}${chalk.reset}`);
211
+ lines.push(`${chalk.gray}${BOX.vertical}${chalk.reset} ${chalk.bold}RUNTIME CONTROL${chalk.reset}${' '.repeat(WIDTH - 17)}${chalk.gray}${BOX.vertical}${chalk.reset}`);
212
+ lines.push(`${chalk.gray}${BOX.vertical}${chalk.reset} ${BOX.lightH.repeat(WIDTH - 4)} ${chalk.gray}${BOX.vertical}${chalk.reset}`);
213
+
214
+ if (isSuccess) {
215
+ lines.push(`${chalk.gray}${BOX.vertical}${chalk.reset} ${chalk.green}[✓] RUNTIME VERIFIED. READY FOR PRODUCTION.${chalk.reset}${' '.repeat(WIDTH - 45)}${chalk.gray}${BOX.vertical}${chalk.reset}`);
216
+ lines.push(`${chalk.gray}${BOX.vertical}${' '.repeat(WIDTH - 2)}${BOX.vertical}${chalk.reset}`);
217
+ lines.push(`${chalk.gray}${BOX.vertical}${chalk.reset} > ${chalk.cyan}vibecheck ship${chalk.reset} [DEPLOY] Proceed with deployment ${chalk.gray}${BOX.vertical}${chalk.reset}`);
218
+ } else {
219
+ lines.push(`${chalk.gray}${BOX.vertical}${chalk.reset} ${chalk.red}[!] VERIFICATION FAILED. REVIEW REQUIRED.${chalk.reset}${' '.repeat(WIDTH - 42)}${chalk.gray}${BOX.vertical}${chalk.reset}`);
220
+ lines.push(`${chalk.gray}${BOX.vertical}${' '.repeat(WIDTH - 2)}${BOX.vertical}${chalk.reset}`);
221
+ lines.push(`${chalk.gray}${BOX.vertical}${chalk.reset} > ${chalk.cyan}vibecheck reality --retry${chalk.reset} [RETRY] Re-run verification ${chalk.gray}${BOX.vertical}${chalk.reset}`);
222
+ lines.push(`${chalk.gray}${BOX.vertical}${chalk.reset} > ${chalk.cyan}vibecheck fix --apply${chalk.reset} [FIX] Apply fixes before retry ${chalk.gray}${BOX.vertical}${chalk.reset}`);
223
+ }
224
+
225
+ // 7. Box Bottom
226
+ lines.push(`${chalk.gray}${BOX.bottomLeft}${BOX.horizontal.repeat(WIDTH - 2)}${BOX.bottomRight}${chalk.reset}`);
227
+
228
+ return lines.join('\n');
229
+ }
230
+
231
+ module.exports = { formatRealityOutput };