@vibecheckai/cli 3.0.2 → 3.0.3
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/package.json +9 -1
- package/bin/cli-hygiene.js +0 -241
- package/bin/guardrail.js +0 -834
- package/bin/runners/cli-utils.js +0 -1070
- package/bin/runners/context/ai-task-decomposer.js +0 -337
- package/bin/runners/context/analyzer.js +0 -462
- package/bin/runners/context/api-contracts.js +0 -427
- package/bin/runners/context/context-diff.js +0 -342
- package/bin/runners/context/context-pruner.js +0 -291
- package/bin/runners/context/dependency-graph.js +0 -414
- package/bin/runners/context/generators/claude.js +0 -107
- package/bin/runners/context/generators/codex.js +0 -108
- package/bin/runners/context/generators/copilot.js +0 -119
- package/bin/runners/context/generators/cursor.js +0 -514
- package/bin/runners/context/generators/mcp.js +0 -151
- package/bin/runners/context/generators/windsurf.js +0 -180
- package/bin/runners/context/git-context.js +0 -302
- package/bin/runners/context/index.js +0 -1042
- package/bin/runners/context/insights.js +0 -173
- package/bin/runners/context/mcp-server/generate-rules.js +0 -337
- package/bin/runners/context/mcp-server/index.js +0 -1176
- package/bin/runners/context/mcp-server/package.json +0 -24
- package/bin/runners/context/memory.js +0 -200
- package/bin/runners/context/monorepo.js +0 -215
- package/bin/runners/context/multi-repo-federation.js +0 -404
- package/bin/runners/context/patterns.js +0 -253
- package/bin/runners/context/proof-context.js +0 -972
- package/bin/runners/context/security-scanner.js +0 -303
- package/bin/runners/context/semantic-search.js +0 -350
- package/bin/runners/context/shared.js +0 -264
- package/bin/runners/context/team-conventions.js +0 -310
- package/bin/runners/lib/ai-bridge.js +0 -416
- package/bin/runners/lib/analysis-core.js +0 -271
- package/bin/runners/lib/analyzers.js +0 -541
- package/bin/runners/lib/audit-bridge.js +0 -391
- package/bin/runners/lib/auth-truth.js +0 -193
- package/bin/runners/lib/auth.js +0 -215
- package/bin/runners/lib/backup.js +0 -62
- package/bin/runners/lib/billing.js +0 -107
- package/bin/runners/lib/claims.js +0 -118
- package/bin/runners/lib/cli-ui.js +0 -540
- package/bin/runners/lib/compliance-bridge-new.js +0 -0
- package/bin/runners/lib/compliance-bridge.js +0 -165
- package/bin/runners/lib/contracts/auth-contract.js +0 -194
- package/bin/runners/lib/contracts/env-contract.js +0 -178
- package/bin/runners/lib/contracts/external-contract.js +0 -198
- package/bin/runners/lib/contracts/guard.js +0 -168
- package/bin/runners/lib/contracts/index.js +0 -89
- package/bin/runners/lib/contracts/plan-validator.js +0 -311
- package/bin/runners/lib/contracts/route-contract.js +0 -192
- package/bin/runners/lib/detect.js +0 -89
- package/bin/runners/lib/doctor/autofix.js +0 -254
- package/bin/runners/lib/doctor/index.js +0 -37
- package/bin/runners/lib/doctor/modules/dependencies.js +0 -325
- package/bin/runners/lib/doctor/modules/index.js +0 -46
- package/bin/runners/lib/doctor/modules/network.js +0 -250
- package/bin/runners/lib/doctor/modules/project.js +0 -312
- package/bin/runners/lib/doctor/modules/runtime.js +0 -224
- package/bin/runners/lib/doctor/modules/security.js +0 -348
- package/bin/runners/lib/doctor/modules/system.js +0 -213
- package/bin/runners/lib/doctor/modules/vibecheck.js +0 -394
- package/bin/runners/lib/doctor/reporter.js +0 -262
- package/bin/runners/lib/doctor/service.js +0 -262
- package/bin/runners/lib/doctor/types.js +0 -113
- package/bin/runners/lib/doctor/ui.js +0 -263
- package/bin/runners/lib/doctor-enhanced.js +0 -233
- package/bin/runners/lib/doctor-v2.js +0 -608
- package/bin/runners/lib/enforcement.js +0 -72
|
@@ -1,262 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* DoctorService — Enterprise-Grade Environment Diagnostics
|
|
3
|
-
*
|
|
4
|
-
* Features:
|
|
5
|
-
* - Pluggable diagnostic modules
|
|
6
|
-
* - Parallel execution for independent checks
|
|
7
|
-
* - Dependency resolution between checks
|
|
8
|
-
* - Auto-fix capability
|
|
9
|
-
* - JSON/Markdown report generation
|
|
10
|
-
* - Health scoring with weighted categories
|
|
11
|
-
* - CI-friendly exit codes
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
const path = require('path');
|
|
15
|
-
const { SEVERITY, CATEGORY, CATEGORY_META } = require('./types');
|
|
16
|
-
const { getAllDiagnostics } = require('./modules');
|
|
17
|
-
const { createSummary, saveReports, generateJsonReport } = require('./reporter');
|
|
18
|
-
const { autoFixAll, getFixCommands } = require('./autofix');
|
|
19
|
-
const ui = require('./ui');
|
|
20
|
-
|
|
21
|
-
class DoctorService {
|
|
22
|
-
constructor(projectPath, options = {}) {
|
|
23
|
-
this.projectPath = path.resolve(projectPath);
|
|
24
|
-
this.options = {
|
|
25
|
-
json: false,
|
|
26
|
-
markdown: false,
|
|
27
|
-
fix: false,
|
|
28
|
-
fixDryRun: false,
|
|
29
|
-
quiet: false,
|
|
30
|
-
verbose: false,
|
|
31
|
-
categories: null, // null = all categories
|
|
32
|
-
skipNetwork: false,
|
|
33
|
-
saveReport: true,
|
|
34
|
-
...options,
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
this.diagnostics = [];
|
|
38
|
-
this.results = [];
|
|
39
|
-
this.startTime = null;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
async run() {
|
|
43
|
-
this.startTime = Date.now();
|
|
44
|
-
|
|
45
|
-
// Load diagnostics
|
|
46
|
-
this.diagnostics = this.loadDiagnostics();
|
|
47
|
-
|
|
48
|
-
// Filter by category if specified
|
|
49
|
-
if (this.options.categories) {
|
|
50
|
-
this.diagnostics = this.diagnostics.filter(d =>
|
|
51
|
-
this.options.categories.includes(d.category)
|
|
52
|
-
);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// Skip network if requested
|
|
56
|
-
if (this.options.skipNetwork) {
|
|
57
|
-
this.diagnostics = this.diagnostics.filter(d => d.category !== CATEGORY.NETWORK);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// Print header (unless quiet or json)
|
|
61
|
-
if (!this.options.quiet && !this.options.json) {
|
|
62
|
-
ui.printHeader();
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// Run diagnostics
|
|
66
|
-
await this.runDiagnostics();
|
|
67
|
-
|
|
68
|
-
// Calculate summary
|
|
69
|
-
const durationMs = Date.now() - this.startTime;
|
|
70
|
-
const summary = createSummary(this.results);
|
|
71
|
-
|
|
72
|
-
// Output results
|
|
73
|
-
if (this.options.json) {
|
|
74
|
-
// JSON output only
|
|
75
|
-
const report = generateJsonReport(this.results, this.projectPath, durationMs);
|
|
76
|
-
console.log(JSON.stringify(report, null, 2));
|
|
77
|
-
} else if (!this.options.quiet) {
|
|
78
|
-
// Print issues and summary
|
|
79
|
-
ui.printIssuesSummary(this.results);
|
|
80
|
-
ui.printFinalSummary(summary, durationMs);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// Save reports
|
|
84
|
-
if (this.options.saveReport && !this.options.json) {
|
|
85
|
-
const outputDir = path.join(this.projectPath, '.vibecheck');
|
|
86
|
-
try {
|
|
87
|
-
const paths = saveReports(this.results, this.projectPath, durationMs, outputDir);
|
|
88
|
-
if (this.options.verbose) {
|
|
89
|
-
console.log(`${ui.c.dim}Reports saved to:${ui.c.reset}`);
|
|
90
|
-
console.log(` ${ui.c.dim}${paths.lastJson}${ui.c.reset}`);
|
|
91
|
-
console.log(` ${ui.c.dim}${paths.lastMd}${ui.c.reset}`);
|
|
92
|
-
}
|
|
93
|
-
} catch (err) {
|
|
94
|
-
if (this.options.verbose) {
|
|
95
|
-
console.log(`${ui.c.dim}Could not save reports: ${err.message}${ui.c.reset}`);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// Auto-fix if requested
|
|
101
|
-
if (this.options.fix) {
|
|
102
|
-
const fixResults = await autoFixAll(this.results, this.projectPath, {
|
|
103
|
-
dryRun: this.options.fixDryRun,
|
|
104
|
-
severity: [SEVERITY.CRITICAL, SEVERITY.ERROR],
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
if (!this.options.quiet && !this.options.json) {
|
|
108
|
-
ui.printAutoFixResults(fixResults);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// Re-run affected checks after fixing
|
|
112
|
-
if (fixResults.succeeded > 0 && !this.options.fixDryRun) {
|
|
113
|
-
if (!this.options.quiet && !this.options.json) {
|
|
114
|
-
console.log(`${ui.c.cyan}Re-running diagnostics after fixes...${ui.c.reset}\n`);
|
|
115
|
-
}
|
|
116
|
-
return this.run();
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// Return exit code
|
|
121
|
-
if (summary.critical > 0) return 3;
|
|
122
|
-
if (summary.errors > 0) return 1;
|
|
123
|
-
if (summary.warnings > 0 && this.options.failOnWarn) return 2;
|
|
124
|
-
return 0;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
loadDiagnostics() {
|
|
128
|
-
return getAllDiagnostics(this.projectPath);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
async runDiagnostics() {
|
|
132
|
-
// Group by category for display
|
|
133
|
-
const byCategory = {};
|
|
134
|
-
for (const d of this.diagnostics) {
|
|
135
|
-
if (!byCategory[d.category]) byCategory[d.category] = [];
|
|
136
|
-
byCategory[d.category].push(d);
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// Sort categories
|
|
140
|
-
const sortedCategories = Object.keys(byCategory).sort((a, b) => {
|
|
141
|
-
const orderA = CATEGORY_META[a]?.order || 99;
|
|
142
|
-
const orderB = CATEGORY_META[b]?.order || 99;
|
|
143
|
-
return orderA - orderB;
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
// Track completed diagnostic IDs for dependency resolution
|
|
147
|
-
const completed = new Set();
|
|
148
|
-
const resultMap = new Map();
|
|
149
|
-
|
|
150
|
-
for (const category of sortedCategories) {
|
|
151
|
-
const categoryDiagnostics = byCategory[category];
|
|
152
|
-
|
|
153
|
-
if (!this.options.quiet && !this.options.json) {
|
|
154
|
-
ui.printCategoryHeader(category);
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
// Separate parallel and sequential diagnostics
|
|
158
|
-
const parallel = categoryDiagnostics.filter(d => d.parallel && !d.dependsOn?.length);
|
|
159
|
-
const sequential = categoryDiagnostics.filter(d => !d.parallel || d.dependsOn?.length);
|
|
160
|
-
|
|
161
|
-
// Run parallel diagnostics
|
|
162
|
-
if (parallel.length > 0) {
|
|
163
|
-
const parallelPromises = parallel.map(d => this.runSingleDiagnostic(d));
|
|
164
|
-
const parallelResults = await Promise.all(parallelPromises);
|
|
165
|
-
|
|
166
|
-
for (let i = 0; i < parallel.length; i++) {
|
|
167
|
-
const diagnostic = parallel[i];
|
|
168
|
-
const result = parallelResults[i];
|
|
169
|
-
|
|
170
|
-
this.results.push(result);
|
|
171
|
-
completed.add(diagnostic.id);
|
|
172
|
-
resultMap.set(diagnostic.id, result);
|
|
173
|
-
|
|
174
|
-
if (!this.options.quiet && !this.options.json) {
|
|
175
|
-
ui.printDiagnostic(result, { showFixes: this.options.verbose });
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
// Run sequential diagnostics (respecting dependencies)
|
|
181
|
-
for (const diagnostic of sequential) {
|
|
182
|
-
// Check dependencies
|
|
183
|
-
if (diagnostic.dependsOn) {
|
|
184
|
-
const unmetDeps = diagnostic.dependsOn.filter(dep => !completed.has(dep));
|
|
185
|
-
if (unmetDeps.length > 0) {
|
|
186
|
-
// Skip if dependencies not met
|
|
187
|
-
const skipResult = {
|
|
188
|
-
...diagnostic,
|
|
189
|
-
severity: SEVERITY.INFO,
|
|
190
|
-
message: `Skipped (depends on: ${unmetDeps.join(', ')})`,
|
|
191
|
-
durationMs: 0,
|
|
192
|
-
};
|
|
193
|
-
this.results.push(skipResult);
|
|
194
|
-
if (!this.options.quiet && !this.options.json) {
|
|
195
|
-
ui.printDiagnostic(skipResult);
|
|
196
|
-
}
|
|
197
|
-
continue;
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
const result = await this.runSingleDiagnostic(diagnostic);
|
|
202
|
-
this.results.push(result);
|
|
203
|
-
completed.add(diagnostic.id);
|
|
204
|
-
resultMap.set(diagnostic.id, result);
|
|
205
|
-
|
|
206
|
-
if (!this.options.quiet && !this.options.json) {
|
|
207
|
-
ui.printDiagnostic(result, { showFixes: this.options.verbose });
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
if (!this.options.quiet && !this.options.json) {
|
|
212
|
-
console.log('');
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
async runSingleDiagnostic(diagnostic) {
|
|
218
|
-
const startTime = Date.now();
|
|
219
|
-
|
|
220
|
-
try {
|
|
221
|
-
const checkResult = await diagnostic.check();
|
|
222
|
-
const durationMs = Date.now() - startTime;
|
|
223
|
-
|
|
224
|
-
return {
|
|
225
|
-
id: diagnostic.id,
|
|
226
|
-
name: diagnostic.name,
|
|
227
|
-
category: diagnostic.category,
|
|
228
|
-
severity: checkResult.severity || SEVERITY.PASS,
|
|
229
|
-
message: checkResult.message || 'OK',
|
|
230
|
-
detail: checkResult.detail,
|
|
231
|
-
durationMs,
|
|
232
|
-
fixes: checkResult.fixes,
|
|
233
|
-
metadata: checkResult.metadata,
|
|
234
|
-
};
|
|
235
|
-
} catch (err) {
|
|
236
|
-
const durationMs = Date.now() - startTime;
|
|
237
|
-
|
|
238
|
-
return {
|
|
239
|
-
id: diagnostic.id,
|
|
240
|
-
name: diagnostic.name,
|
|
241
|
-
category: diagnostic.category,
|
|
242
|
-
severity: SEVERITY.ERROR,
|
|
243
|
-
message: `Check failed: ${err.message}`,
|
|
244
|
-
durationMs,
|
|
245
|
-
};
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
getFixCommands() {
|
|
250
|
-
return getFixCommands(this.results);
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
async function diagnose(projectPath, options = {}) {
|
|
255
|
-
const service = new DoctorService(projectPath, options);
|
|
256
|
-
return service.run();
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
module.exports = {
|
|
260
|
-
DoctorService,
|
|
261
|
-
diagnose,
|
|
262
|
-
};
|
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Doctor Service Types
|
|
3
|
-
*
|
|
4
|
-
* Enterprise-grade diagnostic types and constants
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
const SEVERITY = {
|
|
8
|
-
CRITICAL: 'critical', // Blocks all operations
|
|
9
|
-
ERROR: 'error', // Must fix before using vibecheck
|
|
10
|
-
WARNING: 'warning', // Recommended to fix
|
|
11
|
-
INFO: 'info', // Informational
|
|
12
|
-
PASS: 'pass', // Check passed
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
const SEVERITY_WEIGHT = {
|
|
16
|
-
[SEVERITY.CRITICAL]: 0,
|
|
17
|
-
[SEVERITY.ERROR]: 0.25,
|
|
18
|
-
[SEVERITY.WARNING]: 0.75,
|
|
19
|
-
[SEVERITY.INFO]: 1,
|
|
20
|
-
[SEVERITY.PASS]: 1,
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
const CATEGORY = {
|
|
24
|
-
SYSTEM: 'system',
|
|
25
|
-
RUNTIME: 'runtime',
|
|
26
|
-
PROJECT: 'project',
|
|
27
|
-
SECURITY: 'security',
|
|
28
|
-
NETWORK: 'network',
|
|
29
|
-
DEPENDENCIES: 'dependencies',
|
|
30
|
-
VIBECHECK: 'vibecheck',
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
const CATEGORY_META = {
|
|
34
|
-
[CATEGORY.SYSTEM]: { name: 'System', icon: '💻', order: 1 },
|
|
35
|
-
[CATEGORY.RUNTIME]: { name: 'Runtime', icon: '🟢', order: 2 },
|
|
36
|
-
[CATEGORY.PROJECT]: { name: 'Project', icon: '📁', order: 3 },
|
|
37
|
-
[CATEGORY.DEPENDENCIES]: { name: 'Dependencies', icon: '📦', order: 4 },
|
|
38
|
-
[CATEGORY.SECURITY]: { name: 'Security', icon: '🔐', order: 5 },
|
|
39
|
-
[CATEGORY.NETWORK]: { name: 'Network', icon: '🌐', order: 6 },
|
|
40
|
-
[CATEGORY.VIBECHECK]: { name: 'Vibecheck', icon: '⚡', order: 7 },
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
const FIX_TYPE = {
|
|
44
|
-
COMMAND: 'command', // Run a shell command
|
|
45
|
-
FILE_CREATE: 'file_create',
|
|
46
|
-
FILE_EDIT: 'file_edit',
|
|
47
|
-
MANUAL: 'manual', // User must do manually
|
|
48
|
-
LINK: 'link', // External documentation
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* @typedef {Object} DiagnosticResult
|
|
53
|
-
* @property {string} id - Unique diagnostic ID
|
|
54
|
-
* @property {string} name - Human readable name
|
|
55
|
-
* @property {string} category - Category from CATEGORY enum
|
|
56
|
-
* @property {string} severity - Severity from SEVERITY enum
|
|
57
|
-
* @property {string} message - Short status message
|
|
58
|
-
* @property {string} [detail] - Additional detail
|
|
59
|
-
* @property {number} durationMs - Time taken in milliseconds
|
|
60
|
-
* @property {Fix[]} [fixes] - Available fixes
|
|
61
|
-
* @property {Object} [metadata] - Additional diagnostic data
|
|
62
|
-
*/
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* @typedef {Object} Fix
|
|
66
|
-
* @property {string} type - Fix type from FIX_TYPE enum
|
|
67
|
-
* @property {string} description - What the fix does
|
|
68
|
-
* @property {string} [command] - Command to run (for COMMAND type)
|
|
69
|
-
* @property {string} [path] - File path (for FILE_* types)
|
|
70
|
-
* @property {string} [content] - Content (for FILE_CREATE/EDIT)
|
|
71
|
-
* @property {string} [url] - URL (for LINK type)
|
|
72
|
-
* @property {boolean} [dangerous] - Requires confirmation
|
|
73
|
-
* @property {boolean} [autoFixable] - Can be auto-fixed
|
|
74
|
-
*/
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* @typedef {Object} DiagnosticModule
|
|
78
|
-
* @property {string} id - Module ID
|
|
79
|
-
* @property {string} name - Module name
|
|
80
|
-
* @property {string} category - Category
|
|
81
|
-
* @property {function} check - Async check function
|
|
82
|
-
* @property {boolean} [parallel] - Can run in parallel
|
|
83
|
-
* @property {string[]} [dependsOn] - IDs this module depends on
|
|
84
|
-
*/
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* @typedef {Object} DoctorReport
|
|
88
|
-
* @property {string} version - Report format version
|
|
89
|
-
* @property {string} timestamp - ISO timestamp
|
|
90
|
-
* @property {number} durationMs - Total duration
|
|
91
|
-
* @property {string} projectPath - Scanned project
|
|
92
|
-
* @property {DiagnosticResult[]} diagnostics - All results
|
|
93
|
-
* @property {Summary} summary - Summary stats
|
|
94
|
-
*/
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* @typedef {Object} Summary
|
|
98
|
-
* @property {number} total - Total checks
|
|
99
|
-
* @property {number} passed - Passed checks
|
|
100
|
-
* @property {number} warnings - Warning count
|
|
101
|
-
* @property {number} errors - Error count
|
|
102
|
-
* @property {number} critical - Critical count
|
|
103
|
-
* @property {number} healthScore - 0-100 score
|
|
104
|
-
* @property {string} verdict - HEALTHY | DEGRADED | UNHEALTHY | CRITICAL
|
|
105
|
-
*/
|
|
106
|
-
|
|
107
|
-
module.exports = {
|
|
108
|
-
SEVERITY,
|
|
109
|
-
SEVERITY_WEIGHT,
|
|
110
|
-
CATEGORY,
|
|
111
|
-
CATEGORY_META,
|
|
112
|
-
FIX_TYPE,
|
|
113
|
-
};
|
|
@@ -1,263 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Doctor CLI UI Components
|
|
3
|
-
*
|
|
4
|
-
* Beautiful terminal output for the Doctor service
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
const { SEVERITY, CATEGORY_META } = require('./types');
|
|
8
|
-
|
|
9
|
-
// ANSI color codes
|
|
10
|
-
const c = {
|
|
11
|
-
reset: '\x1b[0m',
|
|
12
|
-
bold: '\x1b[1m',
|
|
13
|
-
dim: '\x1b[2m',
|
|
14
|
-
italic: '\x1b[3m',
|
|
15
|
-
underline: '\x1b[4m',
|
|
16
|
-
|
|
17
|
-
black: '\x1b[30m',
|
|
18
|
-
red: '\x1b[31m',
|
|
19
|
-
green: '\x1b[32m',
|
|
20
|
-
yellow: '\x1b[33m',
|
|
21
|
-
blue: '\x1b[34m',
|
|
22
|
-
magenta: '\x1b[35m',
|
|
23
|
-
cyan: '\x1b[36m',
|
|
24
|
-
white: '\x1b[37m',
|
|
25
|
-
|
|
26
|
-
bgRed: '\x1b[41m',
|
|
27
|
-
bgGreen: '\x1b[42m',
|
|
28
|
-
bgYellow: '\x1b[43m',
|
|
29
|
-
bgBlue: '\x1b[44m',
|
|
30
|
-
bgMagenta: '\x1b[45m',
|
|
31
|
-
bgCyan: '\x1b[46m',
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
const SEVERITY_CONFIG = {
|
|
35
|
-
[SEVERITY.PASS]: { icon: '✓', color: c.green, label: 'PASS' },
|
|
36
|
-
[SEVERITY.INFO]: { icon: 'ℹ', color: c.blue, label: 'INFO' },
|
|
37
|
-
[SEVERITY.WARNING]: { icon: '!', color: c.yellow, label: 'WARN' },
|
|
38
|
-
[SEVERITY.ERROR]: { icon: '✗', color: c.red, label: 'ERROR' },
|
|
39
|
-
[SEVERITY.CRITICAL]: { icon: '⛔', color: `${c.bgRed}${c.white}`, label: 'CRIT' },
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
const BOX_CHARS = {
|
|
43
|
-
topLeft: '╔',
|
|
44
|
-
topRight: '╗',
|
|
45
|
-
bottomLeft: '╚',
|
|
46
|
-
bottomRight: '╝',
|
|
47
|
-
horizontal: '═',
|
|
48
|
-
vertical: '║',
|
|
49
|
-
lightHorizontal: '─',
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
function box(width = 74) {
|
|
53
|
-
return {
|
|
54
|
-
top: `${c.cyan}${BOX_CHARS.topLeft}${BOX_CHARS.horizontal.repeat(width)}${BOX_CHARS.topRight}${c.reset}`,
|
|
55
|
-
bottom: `${c.cyan}${BOX_CHARS.bottomLeft}${BOX_CHARS.horizontal.repeat(width)}${BOX_CHARS.bottomRight}${c.reset}`,
|
|
56
|
-
line: (content, align = 'left') => {
|
|
57
|
-
const stripped = stripAnsi(content);
|
|
58
|
-
const padding = width - stripped.length;
|
|
59
|
-
const left = align === 'center' ? Math.floor(padding / 2) : 0;
|
|
60
|
-
const right = padding - left;
|
|
61
|
-
return `${c.cyan}${BOX_CHARS.vertical}${c.reset}${' '.repeat(left)}${content}${' '.repeat(right)}${c.cyan}${BOX_CHARS.vertical}${c.reset}`;
|
|
62
|
-
},
|
|
63
|
-
empty: `${c.cyan}${BOX_CHARS.vertical}${c.reset}${' '.repeat(width)}${c.cyan}${BOX_CHARS.vertical}${c.reset}`,
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function stripAnsi(str) {
|
|
68
|
-
return str.replace(/\x1b\[[0-9;]*m/g, '');
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
function printHeader() {
|
|
72
|
-
const b = box();
|
|
73
|
-
console.log('');
|
|
74
|
-
console.log(b.top);
|
|
75
|
-
console.log(b.empty);
|
|
76
|
-
console.log(b.line(`${c.bold}🩺 VIBECHECK DOCTOR${c.reset}`, 'center'));
|
|
77
|
-
console.log(b.line(`${c.dim}Enterprise Environment Diagnostics${c.reset}`, 'center'));
|
|
78
|
-
console.log(b.empty);
|
|
79
|
-
console.log(b.bottom);
|
|
80
|
-
console.log('');
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
function printCategoryHeader(category) {
|
|
84
|
-
const meta = CATEGORY_META[category] || { name: category, icon: '📋' };
|
|
85
|
-
console.log(`${c.cyan}${meta.icon} ${c.bold}${meta.name}${c.reset}`);
|
|
86
|
-
console.log(`${c.dim}${BOX_CHARS.lightHorizontal.repeat(50)}${c.reset}`);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
function printDiagnostic(diagnostic, options = {}) {
|
|
90
|
-
const { showDetail = true, showFixes = true, indent = ' ' } = options;
|
|
91
|
-
const config = SEVERITY_CONFIG[diagnostic.severity] || SEVERITY_CONFIG[SEVERITY.INFO];
|
|
92
|
-
|
|
93
|
-
// Main line
|
|
94
|
-
console.log(`${indent}${config.color}${config.icon}${c.reset} ${diagnostic.message}`);
|
|
95
|
-
|
|
96
|
-
// Detail
|
|
97
|
-
if (showDetail && diagnostic.detail) {
|
|
98
|
-
console.log(`${indent} ${c.dim}${diagnostic.detail}${c.reset}`);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// First fix hint
|
|
102
|
-
if (showFixes && diagnostic.fixes && diagnostic.fixes.length > 0) {
|
|
103
|
-
const fix = diagnostic.fixes[0];
|
|
104
|
-
if (fix.command) {
|
|
105
|
-
console.log(`${indent} ${c.cyan}→ ${fix.command}${c.reset}`);
|
|
106
|
-
} else if (fix.url) {
|
|
107
|
-
console.log(`${indent} ${c.cyan}→ ${fix.url}${c.reset}`);
|
|
108
|
-
} else if (fix.description) {
|
|
109
|
-
console.log(`${indent} ${c.cyan}→ ${fix.description}${c.reset}`);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
function printProgress(name, total, current) {
|
|
115
|
-
const percent = Math.round((current / total) * 100);
|
|
116
|
-
const barWidth = 20;
|
|
117
|
-
const filled = Math.round((current / total) * barWidth);
|
|
118
|
-
const bar = '█'.repeat(filled) + '░'.repeat(barWidth - filled);
|
|
119
|
-
|
|
120
|
-
process.stdout.write(`\r${c.dim}${name}${c.reset} [${c.cyan}${bar}${c.reset}] ${percent}%`);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
function clearProgress() {
|
|
124
|
-
process.stdout.write('\r' + ' '.repeat(60) + '\r');
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
function printSpinner(message, frame = 0) {
|
|
128
|
-
const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
129
|
-
process.stdout.write(`\r${c.cyan}${frames[frame % frames.length]}${c.reset} ${message}`);
|
|
130
|
-
return (frame + 1) % frames.length;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
function printIssuesSummary(diagnostics) {
|
|
134
|
-
const critical = diagnostics.filter(d => d.severity === SEVERITY.CRITICAL);
|
|
135
|
-
const errors = diagnostics.filter(d => d.severity === SEVERITY.ERROR);
|
|
136
|
-
const warnings = diagnostics.filter(d => d.severity === SEVERITY.WARNING);
|
|
137
|
-
|
|
138
|
-
if (critical.length === 0 && errors.length === 0 && warnings.length === 0) {
|
|
139
|
-
return;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
const b = box();
|
|
143
|
-
console.log(b.top);
|
|
144
|
-
console.log(b.line(`${c.bold}ISSUES FOUND${c.reset}`));
|
|
145
|
-
console.log(b.bottom);
|
|
146
|
-
console.log('');
|
|
147
|
-
|
|
148
|
-
if (critical.length > 0) {
|
|
149
|
-
console.log(`${c.bgRed}${c.white}${c.bold} CRITICAL (${critical.length}) ${c.reset} — Must fix immediately\n`);
|
|
150
|
-
for (const d of critical) {
|
|
151
|
-
console.log(` ${c.red}●${c.reset} ${c.bold}${d.name}${c.reset}`);
|
|
152
|
-
console.log(` ${c.dim}${d.message}${c.reset}`);
|
|
153
|
-
if (d.fixes?.[0]?.command) {
|
|
154
|
-
console.log(` ${c.cyan}→ ${d.fixes[0].command}${c.reset}`);
|
|
155
|
-
}
|
|
156
|
-
console.log('');
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
if (errors.length > 0) {
|
|
161
|
-
console.log(`${c.red}${c.bold}❌ ERRORS (${errors.length})${c.reset} — Must fix before using vibecheck\n`);
|
|
162
|
-
for (const d of errors) {
|
|
163
|
-
console.log(` ${c.red}●${c.reset} ${c.bold}${d.name}${c.reset}`);
|
|
164
|
-
console.log(` ${c.dim}${d.message}${c.reset}`);
|
|
165
|
-
if (d.fixes?.[0]?.command) {
|
|
166
|
-
console.log(` ${c.cyan}→ ${d.fixes[0].command}${c.reset}`);
|
|
167
|
-
}
|
|
168
|
-
console.log('');
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
if (warnings.length > 0) {
|
|
173
|
-
console.log(`${c.yellow}${c.bold}⚠️ WARNINGS (${warnings.length})${c.reset} — Recommended fixes\n`);
|
|
174
|
-
for (const d of warnings) {
|
|
175
|
-
console.log(` ${c.yellow}●${c.reset} ${c.bold}${d.name}${c.reset}`);
|
|
176
|
-
console.log(` ${c.dim}${d.message}${c.reset}`);
|
|
177
|
-
if (d.fixes?.[0]?.command) {
|
|
178
|
-
console.log(` ${c.cyan}→ ${d.fixes[0].command}${c.reset}`);
|
|
179
|
-
}
|
|
180
|
-
console.log('');
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
function printFinalSummary(summary, durationMs) {
|
|
186
|
-
const b = box();
|
|
187
|
-
|
|
188
|
-
const healthColor = summary.healthScore >= 80 ? c.green
|
|
189
|
-
: summary.healthScore >= 60 ? c.yellow
|
|
190
|
-
: c.red;
|
|
191
|
-
const healthIcon = summary.healthScore >= 80 ? '💚'
|
|
192
|
-
: summary.healthScore >= 60 ? '💛'
|
|
193
|
-
: '❤️';
|
|
194
|
-
|
|
195
|
-
const verdictColor = {
|
|
196
|
-
HEALTHY: c.green,
|
|
197
|
-
DEGRADED: c.yellow,
|
|
198
|
-
UNHEALTHY: c.red,
|
|
199
|
-
CRITICAL: `${c.bgRed}${c.white}`,
|
|
200
|
-
}[summary.verdict] || c.white;
|
|
201
|
-
|
|
202
|
-
console.log(b.top);
|
|
203
|
-
console.log(b.empty);
|
|
204
|
-
console.log(b.line(`${healthIcon} ${c.bold}Health Score: ${healthColor}${summary.healthScore}%${c.reset}`));
|
|
205
|
-
console.log(b.line(` ${c.bold}Verdict: ${verdictColor}${summary.verdict}${c.reset}`));
|
|
206
|
-
console.log(b.empty);
|
|
207
|
-
console.log(b.line(`${c.green}✓ ${summary.passed} passed${c.reset} ${c.blue}ℹ ${summary.info} info${c.reset} ${c.yellow}! ${summary.warnings} warnings${c.reset} ${c.red}✗ ${summary.errors} errors${c.reset} ${summary.critical > 0 ? `${c.bgRed}${c.white}⛔ ${summary.critical}${c.reset}` : ''}`));
|
|
208
|
-
console.log(b.line(`${c.dim}Completed ${summary.total} checks in ${(durationMs / 1000).toFixed(1)}s${c.reset}`));
|
|
209
|
-
console.log(b.empty);
|
|
210
|
-
console.log(b.bottom);
|
|
211
|
-
console.log('');
|
|
212
|
-
|
|
213
|
-
// Final message
|
|
214
|
-
if (summary.critical > 0) {
|
|
215
|
-
console.log(`${c.bgRed}${c.white}${c.bold} ⛔ CRITICAL ISSUES DETECTED ${c.reset}`);
|
|
216
|
-
console.log(`${c.red}Fix critical issues immediately before proceeding.${c.reset}\n`);
|
|
217
|
-
} else if (summary.errors > 0) {
|
|
218
|
-
console.log(`${c.red}${c.bold}⚠ Fix the errors above before running vibecheck.${c.reset}\n`);
|
|
219
|
-
} else if (summary.warnings > 0) {
|
|
220
|
-
console.log(`${c.yellow}Your environment is ready, but consider fixing the warnings.${c.reset}\n`);
|
|
221
|
-
} else {
|
|
222
|
-
console.log(`${c.green}${c.bold}✓ Your environment is perfectly configured!${c.reset}\n`);
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
function printAutoFixResults(results) {
|
|
227
|
-
console.log('');
|
|
228
|
-
console.log(`${c.cyan}${c.bold}🔧 Auto-Fix Results${c.reset}`);
|
|
229
|
-
console.log(`${c.dim}${BOX_CHARS.lightHorizontal.repeat(50)}${c.reset}`);
|
|
230
|
-
|
|
231
|
-
console.log(` Attempted: ${results.attempted}`);
|
|
232
|
-
console.log(` ${c.green}Succeeded: ${results.succeeded}${c.reset}`);
|
|
233
|
-
console.log(` ${c.red}Failed: ${results.failed}${c.reset}`);
|
|
234
|
-
console.log(` ${c.dim}Skipped: ${results.skipped}${c.reset}`);
|
|
235
|
-
console.log('');
|
|
236
|
-
|
|
237
|
-
for (const r of results.results) {
|
|
238
|
-
const icon = r.status === 'success' ? `${c.green}✓${c.reset}`
|
|
239
|
-
: r.status === 'failed' ? `${c.red}✗${c.reset}`
|
|
240
|
-
: `${c.dim}○${c.reset}`;
|
|
241
|
-
console.log(` ${icon} ${r.diagnosticName}: ${r.status}`);
|
|
242
|
-
if (r.error) {
|
|
243
|
-
console.log(` ${c.dim}${r.error}${c.reset}`);
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
console.log('');
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
module.exports = {
|
|
250
|
-
c,
|
|
251
|
-
SEVERITY_CONFIG,
|
|
252
|
-
box,
|
|
253
|
-
stripAnsi,
|
|
254
|
-
printHeader,
|
|
255
|
-
printCategoryHeader,
|
|
256
|
-
printDiagnostic,
|
|
257
|
-
printProgress,
|
|
258
|
-
clearProgress,
|
|
259
|
-
printSpinner,
|
|
260
|
-
printIssuesSummary,
|
|
261
|
-
printFinalSummary,
|
|
262
|
-
printAutoFixResults,
|
|
263
|
-
};
|