@vibecheckai/cli 3.5.5 → 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.
- 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/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 +333 -270
- 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
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Ship Output -
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* -
|
|
6
|
-
* -
|
|
7
|
-
* -
|
|
8
|
-
* - Fix mode display
|
|
9
|
-
* - Badge generation
|
|
2
|
+
* Enterprise Ship Output - V5 "Mission Control" Format
|
|
3
|
+
* Features:
|
|
4
|
+
* - Dynamic "SHIP" vs "NO SHIP" ASCII Art
|
|
5
|
+
* - Deployment Readiness Gauges
|
|
6
|
+
* - AI Hallucination Risk Assessment
|
|
7
|
+
* - Pixel-perfect 80-char alignment
|
|
10
8
|
*/
|
|
11
9
|
|
|
12
10
|
const path = require('path');
|
|
@@ -20,6 +18,119 @@ const {
|
|
|
20
18
|
truncate,
|
|
21
19
|
} = require('./terminal-ui');
|
|
22
20
|
|
|
21
|
+
// ANSI Color Helpers (direct for compatibility)
|
|
22
|
+
const ESC = '\x1b';
|
|
23
|
+
const chalk = {
|
|
24
|
+
reset: `${ESC}[0m`,
|
|
25
|
+
bold: `${ESC}[1m`,
|
|
26
|
+
dim: `${ESC}[2m`,
|
|
27
|
+
red: `${ESC}[31m`,
|
|
28
|
+
green: `${ESC}[32m`,
|
|
29
|
+
yellow: `${ESC}[33m`,
|
|
30
|
+
cyan: `${ESC}[36m`,
|
|
31
|
+
magenta: `${ESC}[35m`,
|
|
32
|
+
white: `${ESC}[37m`,
|
|
33
|
+
gray: `${ESC}[90m`,
|
|
34
|
+
bgRed: `${ESC}[41m`,
|
|
35
|
+
bgGreen: `${ESC}[42m`,
|
|
36
|
+
bgYellow: `${ESC}[43m`,
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
40
|
+
// CONFIGURATION
|
|
41
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
42
|
+
|
|
43
|
+
const WIDTH = 80;
|
|
44
|
+
|
|
45
|
+
const BOX_SHIP = {
|
|
46
|
+
topLeft: '╔', topRight: '╗', bottomLeft: '╚', bottomRight: '╝',
|
|
47
|
+
horizontal: '═', vertical: '║',
|
|
48
|
+
teeRight: '╠', teeLeft: '╣', teeTop: '╤', teeBottom: '╧',
|
|
49
|
+
cross: '╪',
|
|
50
|
+
lightH: '─', lightV: '│',
|
|
51
|
+
lightTeeLeft: '├', lightTeeRight: '┤', lightCross: '┼'
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
// EXTERNAL HEADER
|
|
55
|
+
const LOGO_VIBECHECK = `
|
|
56
|
+
██╗ ██╗██╗██████╗ ███████╗ ██████╗██╗ ██╗███████╗ ██████╗██╗ ██╗
|
|
57
|
+
██║ ██║██║██╔══██╗██╔════╝██╔════╝██║ ██║██╔════╝██╔════╝██║ ██╔╝
|
|
58
|
+
██║ ██║██║██████╔╝█████╗ ██║ ███████║█████╗ ██║ █████╔╝
|
|
59
|
+
╚██╗ ██╔╝██║██╔══██╗██╔══╝ ██║ ██╔══██║██╔══╝ ██║ ██╔═██╗
|
|
60
|
+
╚████╔╝ ██║██████╔╝███████╗╚██████╗██║ ██║███████╗╚██████╗██║ ██╗
|
|
61
|
+
╚═══╝ ╚═╝╚═════╝ ╚══════╝ ╚═════╝╚═╝ ╚═╝╚══════╝ ╚═════╝╚═╝ ╚═╝
|
|
62
|
+
`;
|
|
63
|
+
|
|
64
|
+
// HERO: SHIP (Green)
|
|
65
|
+
const LOGO_SHIP = `
|
|
66
|
+
███████╗██╗ ██╗██╗██████╗
|
|
67
|
+
██╔════╝██║ ██║██║██╔══██╗
|
|
68
|
+
███████╗███████║██║██████╔╝
|
|
69
|
+
╚════██║██╔══██║██║██╔═══╝
|
|
70
|
+
███████║██║ ██║██║██║
|
|
71
|
+
╚══════╝╚═╝ ╚═╝╚═╝╚═╝
|
|
72
|
+
`;
|
|
73
|
+
|
|
74
|
+
// HERO: NO SHIP (Red)
|
|
75
|
+
const LOGO_NOSHIP = `
|
|
76
|
+
███╗ ██╗ ██████╗ ███████╗██╗ ██╗██╗██████╗
|
|
77
|
+
████╗ ██║██╔═══██╗ ██╔════╝██║ ██║██║██╔══██╗
|
|
78
|
+
██╔██╗ ██║██║ ██║ ███████╗███████║██║██████╔╝
|
|
79
|
+
██║╚██╗██║██║ ██║ ╚════██║██╔══██║██║██╔═══╝
|
|
80
|
+
██║ ╚████║╚██████╔╝ ███████║██║ ██║██║██║
|
|
81
|
+
╚═╝ ╚═══╝ ╚═════╝ ╚══════╝╚═╝ ╚═╝╚═╝╚═╝
|
|
82
|
+
`;
|
|
83
|
+
|
|
84
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
85
|
+
// UTILITIES
|
|
86
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
87
|
+
|
|
88
|
+
function stripAnsi(str) {
|
|
89
|
+
return str.replace(/\x1b\[[0-9;]*m/g, '');
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function padCenter(str, width) {
|
|
93
|
+
const visibleLen = stripAnsi(str).length;
|
|
94
|
+
const padding = Math.max(0, width - visibleLen);
|
|
95
|
+
const left = Math.floor(padding / 2);
|
|
96
|
+
const right = padding - left;
|
|
97
|
+
return ' '.repeat(left) + str + ' '.repeat(right);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function padRight(str, len) {
|
|
101
|
+
const visibleLen = stripAnsi(str).length;
|
|
102
|
+
const truncated = visibleLen > len ? str.substring(0, len - 3) + '...' : str;
|
|
103
|
+
return truncated + ' '.repeat(Math.max(0, len - visibleLen));
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function renderHealthBar(percentage, width = 20, colorOverride = null) {
|
|
107
|
+
const fillChar = '█';
|
|
108
|
+
const fadeChars = ['▓', '▒', '░'];
|
|
109
|
+
const emptyChar = '░';
|
|
110
|
+
|
|
111
|
+
const fillAmount = Math.floor((percentage / 100) * width);
|
|
112
|
+
|
|
113
|
+
let barColor = colorOverride;
|
|
114
|
+
if (!barColor) {
|
|
115
|
+
barColor = percentage > 80 ? chalk.green : percentage > 50 ? chalk.yellow : chalk.red;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
let bar = barColor + fillChar.repeat(fillAmount);
|
|
119
|
+
|
|
120
|
+
const remainder = ((percentage / 100) * width) - fillAmount;
|
|
121
|
+
if (fillAmount < width) {
|
|
122
|
+
if (remainder > 0.66) bar += fadeChars[0];
|
|
123
|
+
else if (remainder > 0.33) bar += fadeChars[1];
|
|
124
|
+
else bar += chalk.gray + emptyChar;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const currentVisible = fillAmount + (fillAmount < width ? 1 : 0);
|
|
128
|
+
const emptySpace = Math.max(0, width - currentVisible);
|
|
129
|
+
bar += chalk.gray + emptyChar.repeat(emptySpace) + chalk.reset;
|
|
130
|
+
|
|
131
|
+
return bar;
|
|
132
|
+
}
|
|
133
|
+
|
|
23
134
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
24
135
|
// SHIP-SPECIFIC ICONS
|
|
25
136
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
@@ -765,97 +876,188 @@ function renderUpgradePrompts(findings, verdict) {
|
|
|
765
876
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
766
877
|
|
|
767
878
|
function formatShipOutput(result, options = {}) {
|
|
768
|
-
|
|
769
|
-
verbose = false,
|
|
770
|
-
showFix = false,
|
|
771
|
-
showBadge = false,
|
|
772
|
-
outputDir = '.vibecheck',
|
|
773
|
-
projectPath = '.',
|
|
774
|
-
tier = 'free', // User's current tier
|
|
775
|
-
isVerified = false, // Whether reality testing was done (for verified badge)
|
|
776
|
-
} = options;
|
|
777
|
-
|
|
879
|
+
// 1. Data Prep - Extract from result structure
|
|
778
880
|
const {
|
|
779
881
|
verdict,
|
|
780
|
-
score,
|
|
882
|
+
score = 0,
|
|
781
883
|
findings = [],
|
|
782
884
|
blockers = [],
|
|
783
885
|
warnings = [],
|
|
784
|
-
truthpack,
|
|
785
|
-
proofGraph,
|
|
786
|
-
fixResults,
|
|
787
886
|
duration = 0,
|
|
788
|
-
cached = false,
|
|
789
887
|
} = result;
|
|
790
888
|
|
|
791
|
-
|
|
792
|
-
const
|
|
889
|
+
// Calculate blockers and warnings if not provided
|
|
890
|
+
const actualBlockers = blockers.length > 0
|
|
891
|
+
? blockers
|
|
892
|
+
: findings.filter(f => f.severity === 'BLOCK' || f.severity === 'critical');
|
|
893
|
+
const actualWarnings = warnings.length > 0
|
|
894
|
+
? warnings
|
|
895
|
+
: findings.filter(f => f.severity === 'WARN' || f.severity === 'warning');
|
|
896
|
+
|
|
793
897
|
const canShip = verdict === 'SHIP';
|
|
898
|
+
const logo = canShip ? LOGO_SHIP : LOGO_NOSHIP;
|
|
899
|
+
const themeColor = canShip ? chalk.green : chalk.red;
|
|
900
|
+
const statusText = canShip ? 'CLEAR FOR LAUNCH' : 'DEPLOYMENT ABORTED';
|
|
901
|
+
|
|
902
|
+
// Calculate AI Hallucination Score
|
|
903
|
+
const aiHallucinationFindings = findings.filter(f =>
|
|
904
|
+
f.category === 'MissingRoute' ||
|
|
905
|
+
f.category === 'GhostAuth' ||
|
|
906
|
+
f.category === 'FakeSuccess' ||
|
|
907
|
+
f.type === 'MissingRoute' ||
|
|
908
|
+
f.type === 'GhostAuth'
|
|
909
|
+
);
|
|
910
|
+
const aiScore = Math.max(0, 100 - (aiHallucinationFindings.length * 15));
|
|
911
|
+
|
|
912
|
+
// Generate launch ID
|
|
913
|
+
const launchId = Math.floor(Math.random() * 9999);
|
|
914
|
+
const projectName = path.basename(process.cwd());
|
|
794
915
|
|
|
795
916
|
const lines = [];
|
|
917
|
+
|
|
918
|
+
// 2. Render External Header
|
|
919
|
+
lines.push(chalk.cyan + LOGO_VIBECHECK.trim() + chalk.reset);
|
|
920
|
+
lines.push('');
|
|
921
|
+
|
|
922
|
+
// 3. Render Box Top
|
|
923
|
+
lines.push(`${chalk.gray}${BOX_SHIP.topLeft}${BOX_SHIP.horizontal.repeat(WIDTH - 2)}${BOX_SHIP.topRight}${chalk.reset}`);
|
|
924
|
+
lines.push(`${chalk.gray}${BOX_SHIP.vertical}${' '.repeat(WIDTH - 2)}${BOX_SHIP.vertical}${chalk.reset}`);
|
|
925
|
+
|
|
926
|
+
// 4. Render Hero Logo (SHIP vs NO SHIP)
|
|
927
|
+
logo.trim().split('\n').filter(l => l.trim().length > 0).forEach(l => {
|
|
928
|
+
lines.push(`${chalk.gray}${BOX_SHIP.vertical}${chalk.reset}${themeColor}${padCenter(l.trim(), WIDTH - 2)}${chalk.reset}${chalk.gray}${BOX_SHIP.vertical}${chalk.reset}`);
|
|
929
|
+
});
|
|
930
|
+
lines.push(`${chalk.gray}${BOX_SHIP.vertical}${' '.repeat(WIDTH - 2)}${BOX_SHIP.vertical}${chalk.reset}`);
|
|
931
|
+
|
|
932
|
+
// 5. Info Row
|
|
933
|
+
const version = options.version || 'v3.5.5';
|
|
934
|
+
const integrity = canShip ? `${chalk.green}OPTIMAL${chalk.reset}` : `${chalk.red}COMPROMISED${chalk.reset}`;
|
|
935
|
+
const infoText = `${chalk.bold}${version} SHIP${chalk.reset} :: SYSTEM INTEGRITY: ${integrity}`;
|
|
936
|
+
const metaText = `Launch ID: #${launchId} | T-${duration}ms`;
|
|
937
|
+
const infoLine = ` ${infoText}${' '.repeat(Math.max(1, WIDTH - 6 - stripAnsi(infoText).length - stripAnsi(metaText).length))}${chalk.dim}${metaText}${chalk.reset}`;
|
|
938
|
+
lines.push(`${chalk.gray}${BOX_SHIP.vertical}${chalk.reset}${infoLine} ${chalk.gray}${BOX_SHIP.vertical}${chalk.reset}`);
|
|
939
|
+
|
|
940
|
+
const targetLine = ` Target: ${padRight(projectName, 50)}`;
|
|
941
|
+
lines.push(`${chalk.gray}${BOX_SHIP.vertical}${chalk.reset}${targetLine}${' '.repeat(WIDTH - 2 - stripAnsi(targetLine).length)}${chalk.gray}${BOX_SHIP.vertical}${chalk.reset}`);
|
|
942
|
+
|
|
943
|
+
// 6. Split Pane
|
|
944
|
+
lines.push(`${chalk.gray}${BOX_SHIP.teeRight}${BOX_SHIP.horizontal.repeat(44)}${BOX_SHIP.teeTop}${BOX_SHIP.horizontal.repeat(WIDTH - 47)}${BOX_SHIP.teeLeft}${chalk.reset}`);
|
|
796
945
|
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
blockers: blockers.length || findings.filter(f => f.severity === 'BLOCK' || f.severity === 'critical').length,
|
|
802
|
-
warnings: warnings.length || findings.filter(f => f.severity === 'WARN' || f.severity === 'warning').length,
|
|
803
|
-
duration,
|
|
804
|
-
cached,
|
|
805
|
-
}));
|
|
806
|
-
|
|
807
|
-
// AI Hallucination Score (always show - this is a key selling point)
|
|
808
|
-
lines.push(renderAIHallucinationScore(findings));
|
|
809
|
-
|
|
810
|
-
// Findings breakdown
|
|
811
|
-
lines.push(renderFindingsBreakdown(findings));
|
|
812
|
-
|
|
813
|
-
// Blocker details (with AI attribution, tier-gated fix hints)
|
|
814
|
-
lines.push(renderBlockerDetails(findings, 8, { tier }));
|
|
815
|
-
|
|
816
|
-
// Upgrade prompts for FREE tier users (if there are fixable issues)
|
|
817
|
-
if (findings.length > 0 && !isProTier) {
|
|
818
|
-
lines.push(renderUpgradePrompts(findings, verdict));
|
|
946
|
+
function printSplitRow(left, right) {
|
|
947
|
+
const leftContent = padRight(left || '', 42);
|
|
948
|
+
const rightContent = padRight(right || '', WIDTH - 48);
|
|
949
|
+
lines.push(`${chalk.gray}${BOX_SHIP.vertical}${chalk.reset} ${leftContent} ${chalk.gray}${BOX_SHIP.lightV}${chalk.reset} ${rightContent} ${chalk.gray}${BOX_SHIP.vertical}${chalk.reset}`);
|
|
819
950
|
}
|
|
951
|
+
|
|
952
|
+
// Row 1: Headers
|
|
953
|
+
printSplitRow(`${chalk.bold}DEPLOYMENT VITALS${chalk.reset}`, `${chalk.bold}BLOCKER MANIFEST${chalk.reset}`);
|
|
820
954
|
|
|
821
|
-
//
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
955
|
+
// Row 2: Divider
|
|
956
|
+
lines.push(`${chalk.gray}${BOX_SHIP.vertical}${chalk.reset} ${BOX_SHIP.lightH.repeat(42)} ${chalk.gray}${BOX_SHIP.lightCross}${chalk.reset} ${BOX_SHIP.lightH.repeat(WIDTH - 48)} ${chalk.gray}${BOX_SHIP.vertical}${chalk.reset}`);
|
|
957
|
+
|
|
958
|
+
// Prepare Left Column
|
|
959
|
+
const confidence = canShip ? 99 : Math.max(10, score - 10);
|
|
960
|
+
const leftCol = [
|
|
961
|
+
'',
|
|
962
|
+
` LAUNCH STATUS [${themeColor}${statusText}${chalk.reset}]`,
|
|
963
|
+
` [${renderHealthBar(score, 25, themeColor)}] ${score}%`,
|
|
964
|
+
'',
|
|
965
|
+
` AI REALITY CHECK [${aiScore > 80 ? 'PASSED' : 'RISK'}]`,
|
|
966
|
+
` [${renderHealthBar(aiScore, 25)}] ${aiScore}%`,
|
|
967
|
+
'',
|
|
968
|
+
` CONFIDENCE [${confidence > 90 ? 'HIGH' : 'LOW'}]`,
|
|
969
|
+
` [${renderHealthBar(confidence, 25)}] ${confidence}%`,
|
|
970
|
+
'',
|
|
971
|
+
`${chalk.gray}${BOX_SHIP.lightH.repeat(42)}${chalk.reset}`,
|
|
972
|
+
`${chalk.bold} CERTIFICATION${chalk.reset}`,
|
|
973
|
+
'',
|
|
974
|
+
];
|
|
975
|
+
|
|
976
|
+
// Certification checks
|
|
977
|
+
const hasRouteIssues = findings.some(f => f.category === 'MissingRoute' || f.type === 'MissingRoute');
|
|
978
|
+
const hasAuthIssues = findings.some(f => f.category === 'GhostAuth' || f.category === 'Auth' || f.type === 'Auth');
|
|
979
|
+
const hasEnvIssues = findings.some(f => f.category === 'EnvContract' || f.category === 'EnvGap');
|
|
980
|
+
const hasDbIssues = findings.some(f => f.category === 'Database' || f.type === 'Database');
|
|
981
|
+
|
|
982
|
+
leftCol.push(` ${!hasRouteIssues ? chalk.green + '✓' : chalk.red + '✖'}${chalk.reset} Routes...............${!hasRouteIssues ? 'READY' : 'FAIL'}`);
|
|
983
|
+
leftCol.push(` ${!hasAuthIssues ? chalk.green + '✓' : chalk.red + '✖'}${chalk.reset} Auth Boundaries......${!hasAuthIssues ? 'READY' : 'FAIL'}`);
|
|
984
|
+
leftCol.push(` ${!hasEnvIssues ? chalk.green + '✓' : chalk.red + '✖'}${chalk.reset} Environment..........${!hasEnvIssues ? 'READY' : 'FAIL'}`);
|
|
985
|
+
leftCol.push(` ${!hasDbIssues ? chalk.green + '✓' : chalk.red + '✖'}${chalk.reset} Database.............${!hasDbIssues ? 'READY' : 'FAIL'}`);
|
|
986
|
+
leftCol.push('');
|
|
987
|
+
|
|
988
|
+
// Prepare Right Column
|
|
989
|
+
const rightCol = [];
|
|
990
|
+
rightCol.push('');
|
|
826
991
|
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
992
|
+
if (canShip) {
|
|
993
|
+
rightCol.push(`${chalk.green} [✓] ALL SYSTEMS GO${chalk.reset}`);
|
|
994
|
+
rightCol.push(` No blockers detected.`);
|
|
995
|
+
rightCol.push('');
|
|
996
|
+
rightCol.push(` Ready for production.`);
|
|
997
|
+
rightCol.push(` 0 Blockers`);
|
|
998
|
+
rightCol.push(` ${actualWarnings.length} Warnings (Non-critical)`);
|
|
999
|
+
rightCol.push('');
|
|
1000
|
+
} else {
|
|
1001
|
+
// Show top 3 Blockers or Warnings
|
|
1002
|
+
const topItems = actualBlockers.length > 0 ? actualBlockers.slice(0, 3) : actualWarnings.slice(0, 3);
|
|
1003
|
+
|
|
1004
|
+
if (topItems.length === 0) {
|
|
1005
|
+
rightCol.push(`${chalk.yellow} [!] REVIEW REQUIRED${chalk.reset}`);
|
|
1006
|
+
rightCol.push(` Check findings.`);
|
|
1007
|
+
rightCol.push('');
|
|
1008
|
+
} else {
|
|
1009
|
+
topItems.forEach((f, i) => {
|
|
1010
|
+
const isBlock = f.severity === 'BLOCK' || f.severity === 'critical' || actualBlockers.includes(f);
|
|
1011
|
+
const sevColor = isBlock ? chalk.red : chalk.yellow;
|
|
1012
|
+
const icon = isBlock ? '🛑' : '⚠️';
|
|
1013
|
+
|
|
1014
|
+
const category = f.category || f.type || 'Issue';
|
|
1015
|
+
rightCol.push(`${sevColor}${icon} ${category}${chalk.reset}`);
|
|
1016
|
+
rightCol.push(`${chalk.gray}${BOX_SHIP.lightH.repeat(28)}${chalk.reset}`);
|
|
1017
|
+
|
|
1018
|
+
let file = f.file ? path.basename(f.file) : 'Project';
|
|
1019
|
+
rightCol.push(` ${i+1}. ${file}${f.line ? ':' + f.line : ''}`);
|
|
1020
|
+
|
|
1021
|
+
let msg = f.message || f.title || f.description || 'Unknown issue';
|
|
1022
|
+
if (msg.length > 25) msg = msg.substring(0, 25) + '...';
|
|
1023
|
+
rightCol.push(` > ${chalk.dim}${msg}${chalk.reset}`);
|
|
1024
|
+
rightCol.push('');
|
|
1025
|
+
});
|
|
1026
|
+
|
|
1027
|
+
const remaining = (actualBlockers.length + actualWarnings.length) - 3;
|
|
1028
|
+
if (remaining > 0) {
|
|
1029
|
+
rightCol.push(` ${chalk.dim}... and ${remaining} more items${chalk.reset}`);
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
831
1032
|
}
|
|
832
|
-
|
|
833
|
-
//
|
|
834
|
-
|
|
835
|
-
|
|
1033
|
+
|
|
1034
|
+
// Merge Columns
|
|
1035
|
+
const maxRows = Math.max(leftCol.length, rightCol.length);
|
|
1036
|
+
for (let i = 0; i < maxRows; i++) {
|
|
1037
|
+
printSplitRow(leftCol[i] || '', rightCol[i] || '');
|
|
836
1038
|
}
|
|
837
|
-
|
|
838
|
-
//
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
lines.push('');
|
|
845
|
-
lines.push(
|
|
846
|
-
lines.push(
|
|
847
|
-
lines.push(
|
|
848
|
-
|
|
849
|
-
lines.push('');
|
|
1039
|
+
|
|
1040
|
+
// 7. Action Footer
|
|
1041
|
+
lines.push(`${chalk.gray}${BOX_SHIP.teeRight}${BOX_SHIP.horizontal.repeat(WIDTH - 2)}${BOX_SHIP.teeLeft}${chalk.reset}`);
|
|
1042
|
+
lines.push(`${chalk.gray}${BOX_SHIP.vertical}${chalk.reset} ${chalk.bold}MISSION CONTROL PROTOCOLS${chalk.reset}${' '.repeat(WIDTH - 28)}${chalk.gray}${BOX_SHIP.vertical}${chalk.reset}`);
|
|
1043
|
+
lines.push(`${chalk.gray}${BOX_SHIP.vertical}${chalk.reset} ${BOX_SHIP.lightH.repeat(WIDTH - 4)} ${chalk.gray}${BOX_SHIP.vertical}${chalk.reset}`);
|
|
1044
|
+
|
|
1045
|
+
if (!canShip) {
|
|
1046
|
+
lines.push(`${chalk.gray}${BOX_SHIP.vertical}${chalk.reset} ${chalk.red}[!] LAUNCH SCRUBBED. ${actualBlockers.length} BLOCKERS FOUND.${chalk.reset}${' '.repeat(WIDTH - 42)}${chalk.gray}${BOX_SHIP.vertical}${chalk.reset}`);
|
|
1047
|
+
lines.push(`${chalk.gray}${BOX_SHIP.vertical}${' '.repeat(WIDTH - 2)}${BOX_SHIP.vertical}${chalk.reset}`);
|
|
1048
|
+
lines.push(`${chalk.gray}${BOX_SHIP.vertical}${chalk.reset} > ${chalk.cyan}vibecheck ship --fix${chalk.reset} [AUTO-PATCH] Resolve blockers automatically ${chalk.gray}${BOX_SHIP.vertical}${chalk.reset}`);
|
|
1049
|
+
lines.push(`${chalk.gray}${BOX_SHIP.vertical}${chalk.reset} > ${chalk.cyan}vibecheck report${chalk.reset} [DEBRIEF] View full HTML mission report ${chalk.gray}${BOX_SHIP.vertical}${chalk.reset}`);
|
|
1050
|
+
} else {
|
|
1051
|
+
lines.push(`${chalk.gray}${BOX_SHIP.vertical}${chalk.reset} ${chalk.green}[✓] ORBITAL INSERTION CALCULATED.${chalk.reset}${' '.repeat(WIDTH - 36)}${chalk.gray}${BOX_SHIP.vertical}${chalk.reset}`);
|
|
1052
|
+
lines.push(`${chalk.gray}${BOX_SHIP.vertical}${' '.repeat(WIDTH - 2)}${BOX_SHIP.vertical}${chalk.reset}`);
|
|
1053
|
+
lines.push(`${chalk.gray}${BOX_SHIP.vertical}${chalk.reset} > ${chalk.cyan}git push origin main${chalk.reset} [ENGAGE] Proceed with deployment ${chalk.gray}${BOX_SHIP.vertical}${chalk.reset}`);
|
|
1054
|
+
lines.push(`${chalk.gray}${BOX_SHIP.vertical}${chalk.reset} > ${chalk.cyan}vibecheck prove${chalk.reset} [RECORD] Generate verification proof ${chalk.gray}${BOX_SHIP.vertical}${chalk.reset}`);
|
|
850
1055
|
}
|
|
851
|
-
|
|
852
|
-
//
|
|
853
|
-
lines.push(
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
lines.push(renderNextSteps(canShip, showFix, { tier, showBadge }));
|
|
857
|
-
|
|
858
|
-
return lines.filter(Boolean).join('\n');
|
|
1056
|
+
|
|
1057
|
+
// 8. Box Bottom
|
|
1058
|
+
lines.push(`${chalk.gray}${BOX_SHIP.bottomLeft}${BOX_SHIP.horizontal.repeat(WIDTH - 2)}${BOX_SHIP.bottomRight}${chalk.reset}`);
|
|
1059
|
+
|
|
1060
|
+
return lines.join('\n');
|
|
859
1061
|
}
|
|
860
1062
|
|
|
861
1063
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
@@ -868,18 +1070,6 @@ function renderProgressBar(percent, width, color) {
|
|
|
868
1070
|
return `${color}${'█'.repeat(filled)}${ansi.dim}${'░'.repeat(empty)}${ansi.reset}`;
|
|
869
1071
|
}
|
|
870
1072
|
|
|
871
|
-
function padCenter(str, width) {
|
|
872
|
-
const visibleLen = stripAnsi(str).length;
|
|
873
|
-
const padding = Math.max(0, width - visibleLen);
|
|
874
|
-
const left = Math.floor(padding / 2);
|
|
875
|
-
const right = padding - left;
|
|
876
|
-
return ' '.repeat(left) + str + ' '.repeat(right);
|
|
877
|
-
}
|
|
878
|
-
|
|
879
|
-
function stripAnsi(str) {
|
|
880
|
-
return str.replace(/\x1b\[[0-9;]*m/g, '');
|
|
881
|
-
}
|
|
882
|
-
|
|
883
1073
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
884
1074
|
// EXIT CODES
|
|
885
1075
|
// ═══════════════════════════════════════════════════════════════════════════════
|