@vibecheckai/cli 3.2.2 → 3.2.4
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/.generated +25 -25
- package/bin/dev/run-v2-torture.js +30 -30
- package/bin/runners/ENHANCEMENT_GUIDE.md +121 -121
- package/bin/runners/lib/__tests__/entitlements-v2.test.js +295 -295
- package/bin/runners/lib/agent-firewall/ai/false-positive-analyzer.js +474 -0
- package/bin/runners/lib/agent-firewall/claims/extractor.js +117 -28
- package/bin/runners/lib/agent-firewall/evidence/env-evidence.js +23 -14
- package/bin/runners/lib/agent-firewall/evidence/route-evidence.js +72 -1
- package/bin/runners/lib/agent-firewall/interceptor/base.js +2 -2
- package/bin/runners/lib/agent-firewall/policy/default-policy.json +6 -0
- package/bin/runners/lib/agent-firewall/policy/engine.js +34 -3
- package/bin/runners/lib/agent-firewall/policy/rules/fake-success.js +29 -4
- package/bin/runners/lib/agent-firewall/policy/rules/ghost-route.js +12 -0
- package/bin/runners/lib/agent-firewall/truthpack/loader.js +21 -0
- package/bin/runners/lib/agent-firewall/utils/ignore-checker.js +118 -0
- package/bin/runners/lib/analyzers.js +606 -325
- package/bin/runners/lib/auth-truth.js +193 -193
- package/bin/runners/lib/backup.js +62 -62
- package/bin/runners/lib/billing.js +107 -107
- package/bin/runners/lib/claims.js +118 -118
- package/bin/runners/lib/cli-ui.js +540 -540
- package/bin/runners/lib/contracts/auth-contract.js +202 -202
- package/bin/runners/lib/contracts/env-contract.js +181 -181
- package/bin/runners/lib/contracts/external-contract.js +206 -206
- package/bin/runners/lib/contracts/guard.js +168 -168
- package/bin/runners/lib/contracts/index.js +89 -89
- package/bin/runners/lib/contracts/plan-validator.js +311 -311
- package/bin/runners/lib/contracts/route-contract.js +199 -199
- package/bin/runners/lib/contracts.js +804 -804
- package/bin/runners/lib/detect.js +89 -89
- package/bin/runners/lib/doctor/autofix.js +254 -254
- package/bin/runners/lib/doctor/index.js +37 -37
- package/bin/runners/lib/doctor/modules/dependencies.js +325 -325
- package/bin/runners/lib/doctor/modules/index.js +46 -46
- package/bin/runners/lib/doctor/modules/network.js +250 -250
- package/bin/runners/lib/doctor/modules/project.js +312 -312
- package/bin/runners/lib/doctor/modules/runtime.js +224 -224
- package/bin/runners/lib/doctor/modules/security.js +348 -348
- package/bin/runners/lib/doctor/modules/system.js +213 -213
- package/bin/runners/lib/doctor/modules/vibecheck.js +394 -394
- package/bin/runners/lib/doctor/reporter.js +262 -262
- package/bin/runners/lib/doctor/service.js +262 -262
- package/bin/runners/lib/doctor/types.js +113 -113
- package/bin/runners/lib/doctor/ui.js +263 -263
- package/bin/runners/lib/doctor-v2.js +608 -608
- package/bin/runners/lib/drift.js +425 -425
- package/bin/runners/lib/enforcement.js +72 -72
- package/bin/runners/lib/engines/accessibility-engine.js +190 -0
- package/bin/runners/lib/engines/api-consistency-engine.js +162 -0
- package/bin/runners/lib/engines/ast-cache.js +99 -0
- package/bin/runners/lib/engines/code-quality-engine.js +255 -0
- package/bin/runners/lib/engines/console-logs-engine.js +115 -0
- package/bin/runners/lib/engines/cross-file-analysis-engine.js +268 -0
- package/bin/runners/lib/engines/dead-code-engine.js +198 -0
- package/bin/runners/lib/engines/deprecated-api-engine.js +226 -0
- package/bin/runners/lib/engines/empty-catch-engine.js +150 -0
- package/bin/runners/lib/engines/file-filter.js +131 -0
- package/bin/runners/lib/engines/hardcoded-secrets-engine.js +251 -0
- package/bin/runners/lib/engines/mock-data-engine.js +272 -0
- package/bin/runners/lib/engines/parallel-processor.js +71 -0
- package/bin/runners/lib/engines/performance-issues-engine.js +265 -0
- package/bin/runners/lib/engines/security-vulnerabilities-engine.js +243 -0
- package/bin/runners/lib/engines/todo-fixme-engine.js +115 -0
- package/bin/runners/lib/engines/type-aware-engine.js +152 -0
- package/bin/runners/lib/engines/unsafe-regex-engine.js +225 -0
- package/bin/runners/lib/engines/vibecheck-engines/README.md +53 -0
- package/bin/runners/lib/engines/vibecheck-engines/index.js +15 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/ast-cache.js +164 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/code-quality-engine.js +291 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/console-logs-engine.js +83 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/dead-code-engine.js +198 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/deprecated-api-engine.js +275 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/empty-catch-engine.js +167 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/file-filter.js +217 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/hardcoded-secrets-engine.js +139 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/mock-data-engine.js +140 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/parallel-processor.js +164 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/performance-issues-engine.js +234 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/type-aware-engine.js +217 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/unsafe-regex-engine.js +78 -0
- package/bin/runners/lib/engines/vibecheck-engines/package.json +13 -0
- package/bin/runners/lib/enterprise-detect.js +603 -603
- package/bin/runners/lib/enterprise-init.js +942 -942
- package/bin/runners/lib/env-resolver.js +417 -417
- package/bin/runners/lib/env-template.js +66 -66
- package/bin/runners/lib/env.js +189 -189
- package/bin/runners/lib/extractors/client-calls.js +990 -990
- package/bin/runners/lib/extractors/fastify-route-dump.js +573 -573
- package/bin/runners/lib/extractors/fastify-routes.js +426 -426
- package/bin/runners/lib/extractors/index.js +363 -363
- package/bin/runners/lib/extractors/next-routes.js +524 -524
- package/bin/runners/lib/extractors/proof-graph.js +431 -431
- package/bin/runners/lib/extractors/route-matcher.js +451 -451
- package/bin/runners/lib/extractors/truthpack-v2.js +377 -377
- package/bin/runners/lib/extractors/ui-bindings.js +547 -547
- package/bin/runners/lib/findings-schema.js +281 -281
- package/bin/runners/lib/firewall-prompt.js +50 -50
- package/bin/runners/lib/global-flags.js +213 -213
- package/bin/runners/lib/graph/graph-builder.js +265 -265
- package/bin/runners/lib/graph/html-renderer.js +413 -413
- package/bin/runners/lib/graph/index.js +32 -32
- package/bin/runners/lib/graph/runtime-collector.js +215 -215
- package/bin/runners/lib/graph/static-extractor.js +518 -518
- package/bin/runners/lib/html-report.js +650 -650
- package/bin/runners/lib/interactive-menu.js +1496 -1496
- package/bin/runners/lib/llm.js +75 -75
- package/bin/runners/lib/meter.js +61 -61
- package/bin/runners/lib/missions/evidence.js +126 -126
- package/bin/runners/lib/patch.js +40 -40
- package/bin/runners/lib/permissions/auth-model.js +213 -213
- package/bin/runners/lib/permissions/idor-prover.js +205 -205
- package/bin/runners/lib/permissions/index.js +45 -45
- package/bin/runners/lib/permissions/matrix-builder.js +198 -198
- package/bin/runners/lib/pkgjson.js +28 -28
- package/bin/runners/lib/policy.js +295 -295
- package/bin/runners/lib/preflight.js +142 -142
- package/bin/runners/lib/reality/correlation-detectors.js +359 -359
- package/bin/runners/lib/reality/index.js +318 -318
- package/bin/runners/lib/reality/request-hashing.js +416 -416
- package/bin/runners/lib/reality/request-mapper.js +453 -453
- package/bin/runners/lib/reality/safety-rails.js +463 -463
- package/bin/runners/lib/reality/semantic-snapshot.js +408 -408
- package/bin/runners/lib/reality/toast-detector.js +393 -393
- package/bin/runners/lib/reality-findings.js +84 -84
- package/bin/runners/lib/receipts.js +179 -179
- package/bin/runners/lib/redact.js +29 -29
- package/bin/runners/lib/replay/capsule-manager.js +154 -154
- package/bin/runners/lib/replay/index.js +263 -263
- package/bin/runners/lib/replay/player.js +348 -348
- package/bin/runners/lib/replay/recorder.js +331 -331
- package/bin/runners/lib/report-output.js +187 -187
- package/bin/runners/lib/report.js +135 -135
- package/bin/runners/lib/route-detection.js +1140 -1140
- package/bin/runners/lib/sandbox/index.js +59 -59
- package/bin/runners/lib/sandbox/proof-chain.js +399 -399
- package/bin/runners/lib/sandbox/sandbox-runner.js +205 -205
- package/bin/runners/lib/sandbox/worktree.js +174 -174
- package/bin/runners/lib/scan-output.js +525 -190
- package/bin/runners/lib/schema-validator.js +350 -350
- package/bin/runners/lib/schemas/contracts.schema.json +160 -160
- package/bin/runners/lib/schemas/finding.schema.json +100 -100
- package/bin/runners/lib/schemas/mission-pack.schema.json +206 -206
- package/bin/runners/lib/schemas/proof-graph.schema.json +176 -176
- package/bin/runners/lib/schemas/reality-report.schema.json +162 -162
- package/bin/runners/lib/schemas/share-pack.schema.json +180 -180
- package/bin/runners/lib/schemas/ship-report.schema.json +117 -117
- package/bin/runners/lib/schemas/truthpack-v2.schema.json +303 -303
- package/bin/runners/lib/schemas/validator.js +438 -438
- package/bin/runners/lib/score-history.js +282 -282
- package/bin/runners/lib/share-pack.js +239 -239
- package/bin/runners/lib/snippets.js +67 -67
- package/bin/runners/lib/status-output.js +253 -253
- package/bin/runners/lib/terminal-ui.js +351 -271
- package/bin/runners/lib/upsell.js +510 -510
- package/bin/runners/lib/usage.js +153 -153
- package/bin/runners/lib/validate-patch.js +156 -156
- package/bin/runners/lib/verdict-engine.js +628 -628
- package/bin/runners/reality/engine.js +917 -917
- package/bin/runners/reality/flows.js +122 -122
- package/bin/runners/reality/report.js +378 -378
- package/bin/runners/reality/session.js +193 -193
- package/bin/runners/runGuard.js +168 -168
- package/bin/runners/runProof.zip +0 -0
- package/bin/runners/runProve.js +8 -0
- package/bin/runners/runReality.js +14 -0
- package/bin/runners/runScan.js +17 -1
- package/bin/runners/runTruth.js +15 -3
- package/mcp-server/tier-auth.js +4 -4
- package/mcp-server/tools/index.js +72 -72
- package/package.json +1 -1
|
@@ -1,262 +1,262 @@
|
|
|
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
|
+
/**
|
|
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
|
+
};
|