@vibecheckai/cli 3.2.4 → 3.2.6
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/lib/__tests__/entitlements-v2.test.js +295 -295
- package/bin/runners/lib/api-client.js +269 -0
- 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/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/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/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.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/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/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/runAgent.d.ts +5 -0
- package/bin/runners/runFirewall.d.ts +5 -0
- package/bin/runners/runFirewallHook.d.ts +5 -0
- package/bin/runners/runGuard.js +168 -168
- package/bin/runners/runScan.js +82 -0
- package/bin/runners/runTruth.d.ts +5 -0
- package/bin/vibecheck.js +45 -20
- package/mcp-server/index.js +85 -0
- package/mcp-server/lib/api-client.js +269 -0
- package/mcp-server/package.json +1 -1
- package/mcp-server/tier-auth.js +173 -113
- package/mcp-server/tools/index.js +72 -72
- package/mcp-server/vibecheck-mcp-server-3.2.0.tgz +0 -0
- package/package.json +1 -1
|
@@ -1,262 +1,262 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Doctor Report Generator
|
|
3
|
-
*
|
|
4
|
-
* Generates JSON and Markdown reports from diagnostic results
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
const fs = require('fs');
|
|
8
|
-
const path = require('path');
|
|
9
|
-
const { SEVERITY, SEVERITY_WEIGHT, CATEGORY_META } = require('./types');
|
|
10
|
-
|
|
11
|
-
const REPORT_VERSION = '2.0.0';
|
|
12
|
-
|
|
13
|
-
function calculateHealthScore(diagnostics) {
|
|
14
|
-
if (diagnostics.length === 0) return 100;
|
|
15
|
-
|
|
16
|
-
let weightedSum = 0;
|
|
17
|
-
let totalWeight = 0;
|
|
18
|
-
|
|
19
|
-
for (const d of diagnostics) {
|
|
20
|
-
const weight = d.category === 'security' ? 2 : 1; // Security issues count more
|
|
21
|
-
weightedSum += (SEVERITY_WEIGHT[d.severity] || 1) * weight;
|
|
22
|
-
totalWeight += weight;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
return Math.round((weightedSum / totalWeight) * 100);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function getVerdict(healthScore, diagnostics) {
|
|
29
|
-
const criticalCount = diagnostics.filter(d => d.severity === SEVERITY.CRITICAL).length;
|
|
30
|
-
const errorCount = diagnostics.filter(d => d.severity === SEVERITY.ERROR).length;
|
|
31
|
-
|
|
32
|
-
if (criticalCount > 0) return 'CRITICAL';
|
|
33
|
-
if (errorCount > 0) return 'UNHEALTHY';
|
|
34
|
-
if (healthScore < 80) return 'DEGRADED';
|
|
35
|
-
return 'HEALTHY';
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function createSummary(diagnostics) {
|
|
39
|
-
const total = diagnostics.length;
|
|
40
|
-
const passed = diagnostics.filter(d => d.severity === SEVERITY.PASS).length;
|
|
41
|
-
const info = diagnostics.filter(d => d.severity === SEVERITY.INFO).length;
|
|
42
|
-
const warnings = diagnostics.filter(d => d.severity === SEVERITY.WARNING).length;
|
|
43
|
-
const errors = diagnostics.filter(d => d.severity === SEVERITY.ERROR).length;
|
|
44
|
-
const critical = diagnostics.filter(d => d.severity === SEVERITY.CRITICAL).length;
|
|
45
|
-
|
|
46
|
-
const healthScore = calculateHealthScore(diagnostics);
|
|
47
|
-
const verdict = getVerdict(healthScore, diagnostics);
|
|
48
|
-
|
|
49
|
-
return {
|
|
50
|
-
total,
|
|
51
|
-
passed,
|
|
52
|
-
info,
|
|
53
|
-
warnings,
|
|
54
|
-
errors,
|
|
55
|
-
critical,
|
|
56
|
-
healthScore,
|
|
57
|
-
verdict,
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
function generateJsonReport(diagnostics, projectPath, durationMs) {
|
|
62
|
-
const summary = createSummary(diagnostics);
|
|
63
|
-
|
|
64
|
-
return {
|
|
65
|
-
version: REPORT_VERSION,
|
|
66
|
-
timestamp: new Date().toISOString(),
|
|
67
|
-
durationMs,
|
|
68
|
-
projectPath,
|
|
69
|
-
summary,
|
|
70
|
-
diagnostics: diagnostics.map(d => ({
|
|
71
|
-
id: d.id,
|
|
72
|
-
name: d.name,
|
|
73
|
-
category: d.category,
|
|
74
|
-
severity: d.severity,
|
|
75
|
-
message: d.message,
|
|
76
|
-
detail: d.detail,
|
|
77
|
-
durationMs: d.durationMs,
|
|
78
|
-
fixes: d.fixes,
|
|
79
|
-
metadata: d.metadata,
|
|
80
|
-
})),
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
function generateMarkdownReport(diagnostics, projectPath, durationMs) {
|
|
85
|
-
const summary = createSummary(diagnostics);
|
|
86
|
-
const lines = [];
|
|
87
|
-
|
|
88
|
-
lines.push('# 🩺 Vibecheck Doctor Report');
|
|
89
|
-
lines.push('');
|
|
90
|
-
lines.push(`**Project:** \`${projectPath}\` `);
|
|
91
|
-
lines.push(`**Generated:** ${new Date().toISOString()} `);
|
|
92
|
-
lines.push(`**Duration:** ${(durationMs / 1000).toFixed(1)}s`);
|
|
93
|
-
lines.push('');
|
|
94
|
-
|
|
95
|
-
// Summary Box
|
|
96
|
-
lines.push('## Summary');
|
|
97
|
-
lines.push('');
|
|
98
|
-
lines.push(`| Metric | Value |`);
|
|
99
|
-
lines.push(`|--------|-------|`);
|
|
100
|
-
lines.push(`| Health Score | **${summary.healthScore}%** |`);
|
|
101
|
-
lines.push(`| Verdict | **${summary.verdict}** |`);
|
|
102
|
-
lines.push(`| Total Checks | ${summary.total} |`);
|
|
103
|
-
lines.push(`| ✓ Passed | ${summary.passed} |`);
|
|
104
|
-
lines.push(`| ℹ Info | ${summary.info} |`);
|
|
105
|
-
lines.push(`| ⚠ Warnings | ${summary.warnings} |`);
|
|
106
|
-
lines.push(`| ✗ Errors | ${summary.errors} |`);
|
|
107
|
-
lines.push(`| 🔴 Critical | ${summary.critical} |`);
|
|
108
|
-
lines.push('');
|
|
109
|
-
|
|
110
|
-
// Group by category
|
|
111
|
-
const byCategory = {};
|
|
112
|
-
for (const d of diagnostics) {
|
|
113
|
-
if (!byCategory[d.category]) byCategory[d.category] = [];
|
|
114
|
-
byCategory[d.category].push(d);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
// Sort categories by order
|
|
118
|
-
const sortedCategories = Object.keys(byCategory).sort((a, b) => {
|
|
119
|
-
const orderA = CATEGORY_META[a]?.order || 99;
|
|
120
|
-
const orderB = CATEGORY_META[b]?.order || 99;
|
|
121
|
-
return orderA - orderB;
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
lines.push('## Diagnostics');
|
|
125
|
-
lines.push('');
|
|
126
|
-
|
|
127
|
-
for (const category of sortedCategories) {
|
|
128
|
-
const meta = CATEGORY_META[category] || { name: category, icon: '📋' };
|
|
129
|
-
lines.push(`### ${meta.icon} ${meta.name}`);
|
|
130
|
-
lines.push('');
|
|
131
|
-
|
|
132
|
-
for (const d of byCategory[category]) {
|
|
133
|
-
const icon = {
|
|
134
|
-
[SEVERITY.PASS]: '✓',
|
|
135
|
-
[SEVERITY.INFO]: 'ℹ',
|
|
136
|
-
[SEVERITY.WARNING]: '⚠',
|
|
137
|
-
[SEVERITY.ERROR]: '✗',
|
|
138
|
-
[SEVERITY.CRITICAL]: '🔴',
|
|
139
|
-
}[d.severity] || '•';
|
|
140
|
-
|
|
141
|
-
lines.push(`- ${icon} **${d.name}**: ${d.message}`);
|
|
142
|
-
|
|
143
|
-
if (d.detail) {
|
|
144
|
-
lines.push(` - ${d.detail}`);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
if (d.fixes && d.fixes.length > 0) {
|
|
148
|
-
for (const fix of d.fixes) {
|
|
149
|
-
if (fix.command) {
|
|
150
|
-
lines.push(` - Fix: \`${fix.command}\``);
|
|
151
|
-
} else if (fix.url) {
|
|
152
|
-
lines.push(` - [${fix.description}](${fix.url})`);
|
|
153
|
-
} else if (fix.description) {
|
|
154
|
-
lines.push(` - Fix: ${fix.description}`);
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
lines.push('');
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
// Issues section
|
|
163
|
-
const issues = diagnostics.filter(d =>
|
|
164
|
-
d.severity === SEVERITY.CRITICAL ||
|
|
165
|
-
d.severity === SEVERITY.ERROR ||
|
|
166
|
-
d.severity === SEVERITY.WARNING
|
|
167
|
-
);
|
|
168
|
-
|
|
169
|
-
if (issues.length > 0) {
|
|
170
|
-
lines.push('## Issues to Fix');
|
|
171
|
-
lines.push('');
|
|
172
|
-
|
|
173
|
-
const critical = issues.filter(d => d.severity === SEVERITY.CRITICAL);
|
|
174
|
-
const errors = issues.filter(d => d.severity === SEVERITY.ERROR);
|
|
175
|
-
const warnings = issues.filter(d => d.severity === SEVERITY.WARNING);
|
|
176
|
-
|
|
177
|
-
if (critical.length > 0) {
|
|
178
|
-
lines.push('### 🔴 Critical');
|
|
179
|
-
for (const d of critical) {
|
|
180
|
-
lines.push(`1. **${d.name}**: ${d.message}`);
|
|
181
|
-
if (d.fixes?.[0]?.command) {
|
|
182
|
-
lines.push(` \`\`\`bash`);
|
|
183
|
-
lines.push(` ${d.fixes[0].command}`);
|
|
184
|
-
lines.push(` \`\`\``);
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
lines.push('');
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
if (errors.length > 0) {
|
|
191
|
-
lines.push('### ✗ Errors');
|
|
192
|
-
for (const d of errors) {
|
|
193
|
-
lines.push(`1. **${d.name}**: ${d.message}`);
|
|
194
|
-
if (d.fixes?.[0]?.command) {
|
|
195
|
-
lines.push(` \`\`\`bash`);
|
|
196
|
-
lines.push(` ${d.fixes[0].command}`);
|
|
197
|
-
lines.push(` \`\`\``);
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
lines.push('');
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
if (warnings.length > 0) {
|
|
204
|
-
lines.push('### ⚠ Warnings');
|
|
205
|
-
for (const d of warnings) {
|
|
206
|
-
lines.push(`1. **${d.name}**: ${d.message}`);
|
|
207
|
-
if (d.fixes?.[0]?.command) {
|
|
208
|
-
lines.push(` \`\`\`bash`);
|
|
209
|
-
lines.push(` ${d.fixes[0].command}`);
|
|
210
|
-
lines.push(` \`\`\``);
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
lines.push('');
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
lines.push('---');
|
|
218
|
-
lines.push(`*Generated by vibecheck doctor v${REPORT_VERSION}*`);
|
|
219
|
-
|
|
220
|
-
return lines.join('\n');
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
function saveReports(diagnostics, projectPath, durationMs, outputDir) {
|
|
224
|
-
const jsonReport = generateJsonReport(diagnostics, projectPath, durationMs);
|
|
225
|
-
const mdReport = generateMarkdownReport(diagnostics, projectPath, durationMs);
|
|
226
|
-
|
|
227
|
-
// Ensure output directory exists
|
|
228
|
-
const doctorDir = path.join(outputDir, 'doctor');
|
|
229
|
-
fs.mkdirSync(doctorDir, { recursive: true });
|
|
230
|
-
|
|
231
|
-
// Save timestamped reports
|
|
232
|
-
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
|
|
233
|
-
const jsonPath = path.join(doctorDir, `doctor_${timestamp}.json`);
|
|
234
|
-
const mdPath = path.join(doctorDir, `doctor_${timestamp}.md`);
|
|
235
|
-
|
|
236
|
-
fs.writeFileSync(jsonPath, JSON.stringify(jsonReport, null, 2));
|
|
237
|
-
fs.writeFileSync(mdPath, mdReport);
|
|
238
|
-
|
|
239
|
-
// Save stable pointers
|
|
240
|
-
const lastJsonPath = path.join(doctorDir, 'last_doctor.json');
|
|
241
|
-
const lastMdPath = path.join(doctorDir, 'last_doctor.md');
|
|
242
|
-
|
|
243
|
-
fs.writeFileSync(lastJsonPath, JSON.stringify(jsonReport, null, 2));
|
|
244
|
-
fs.writeFileSync(lastMdPath, mdReport);
|
|
245
|
-
|
|
246
|
-
return {
|
|
247
|
-
json: jsonPath,
|
|
248
|
-
md: mdPath,
|
|
249
|
-
lastJson: lastJsonPath,
|
|
250
|
-
lastMd: lastMdPath,
|
|
251
|
-
};
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
module.exports = {
|
|
255
|
-
REPORT_VERSION,
|
|
256
|
-
calculateHealthScore,
|
|
257
|
-
getVerdict,
|
|
258
|
-
createSummary,
|
|
259
|
-
generateJsonReport,
|
|
260
|
-
generateMarkdownReport,
|
|
261
|
-
saveReports,
|
|
262
|
-
};
|
|
1
|
+
/**
|
|
2
|
+
* Doctor Report Generator
|
|
3
|
+
*
|
|
4
|
+
* Generates JSON and Markdown reports from diagnostic results
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
const { SEVERITY, SEVERITY_WEIGHT, CATEGORY_META } = require('./types');
|
|
10
|
+
|
|
11
|
+
const REPORT_VERSION = '2.0.0';
|
|
12
|
+
|
|
13
|
+
function calculateHealthScore(diagnostics) {
|
|
14
|
+
if (diagnostics.length === 0) return 100;
|
|
15
|
+
|
|
16
|
+
let weightedSum = 0;
|
|
17
|
+
let totalWeight = 0;
|
|
18
|
+
|
|
19
|
+
for (const d of diagnostics) {
|
|
20
|
+
const weight = d.category === 'security' ? 2 : 1; // Security issues count more
|
|
21
|
+
weightedSum += (SEVERITY_WEIGHT[d.severity] || 1) * weight;
|
|
22
|
+
totalWeight += weight;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return Math.round((weightedSum / totalWeight) * 100);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function getVerdict(healthScore, diagnostics) {
|
|
29
|
+
const criticalCount = diagnostics.filter(d => d.severity === SEVERITY.CRITICAL).length;
|
|
30
|
+
const errorCount = diagnostics.filter(d => d.severity === SEVERITY.ERROR).length;
|
|
31
|
+
|
|
32
|
+
if (criticalCount > 0) return 'CRITICAL';
|
|
33
|
+
if (errorCount > 0) return 'UNHEALTHY';
|
|
34
|
+
if (healthScore < 80) return 'DEGRADED';
|
|
35
|
+
return 'HEALTHY';
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function createSummary(diagnostics) {
|
|
39
|
+
const total = diagnostics.length;
|
|
40
|
+
const passed = diagnostics.filter(d => d.severity === SEVERITY.PASS).length;
|
|
41
|
+
const info = diagnostics.filter(d => d.severity === SEVERITY.INFO).length;
|
|
42
|
+
const warnings = diagnostics.filter(d => d.severity === SEVERITY.WARNING).length;
|
|
43
|
+
const errors = diagnostics.filter(d => d.severity === SEVERITY.ERROR).length;
|
|
44
|
+
const critical = diagnostics.filter(d => d.severity === SEVERITY.CRITICAL).length;
|
|
45
|
+
|
|
46
|
+
const healthScore = calculateHealthScore(diagnostics);
|
|
47
|
+
const verdict = getVerdict(healthScore, diagnostics);
|
|
48
|
+
|
|
49
|
+
return {
|
|
50
|
+
total,
|
|
51
|
+
passed,
|
|
52
|
+
info,
|
|
53
|
+
warnings,
|
|
54
|
+
errors,
|
|
55
|
+
critical,
|
|
56
|
+
healthScore,
|
|
57
|
+
verdict,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function generateJsonReport(diagnostics, projectPath, durationMs) {
|
|
62
|
+
const summary = createSummary(diagnostics);
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
version: REPORT_VERSION,
|
|
66
|
+
timestamp: new Date().toISOString(),
|
|
67
|
+
durationMs,
|
|
68
|
+
projectPath,
|
|
69
|
+
summary,
|
|
70
|
+
diagnostics: diagnostics.map(d => ({
|
|
71
|
+
id: d.id,
|
|
72
|
+
name: d.name,
|
|
73
|
+
category: d.category,
|
|
74
|
+
severity: d.severity,
|
|
75
|
+
message: d.message,
|
|
76
|
+
detail: d.detail,
|
|
77
|
+
durationMs: d.durationMs,
|
|
78
|
+
fixes: d.fixes,
|
|
79
|
+
metadata: d.metadata,
|
|
80
|
+
})),
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function generateMarkdownReport(diagnostics, projectPath, durationMs) {
|
|
85
|
+
const summary = createSummary(diagnostics);
|
|
86
|
+
const lines = [];
|
|
87
|
+
|
|
88
|
+
lines.push('# 🩺 Vibecheck Doctor Report');
|
|
89
|
+
lines.push('');
|
|
90
|
+
lines.push(`**Project:** \`${projectPath}\` `);
|
|
91
|
+
lines.push(`**Generated:** ${new Date().toISOString()} `);
|
|
92
|
+
lines.push(`**Duration:** ${(durationMs / 1000).toFixed(1)}s`);
|
|
93
|
+
lines.push('');
|
|
94
|
+
|
|
95
|
+
// Summary Box
|
|
96
|
+
lines.push('## Summary');
|
|
97
|
+
lines.push('');
|
|
98
|
+
lines.push(`| Metric | Value |`);
|
|
99
|
+
lines.push(`|--------|-------|`);
|
|
100
|
+
lines.push(`| Health Score | **${summary.healthScore}%** |`);
|
|
101
|
+
lines.push(`| Verdict | **${summary.verdict}** |`);
|
|
102
|
+
lines.push(`| Total Checks | ${summary.total} |`);
|
|
103
|
+
lines.push(`| ✓ Passed | ${summary.passed} |`);
|
|
104
|
+
lines.push(`| ℹ Info | ${summary.info} |`);
|
|
105
|
+
lines.push(`| ⚠ Warnings | ${summary.warnings} |`);
|
|
106
|
+
lines.push(`| ✗ Errors | ${summary.errors} |`);
|
|
107
|
+
lines.push(`| 🔴 Critical | ${summary.critical} |`);
|
|
108
|
+
lines.push('');
|
|
109
|
+
|
|
110
|
+
// Group by category
|
|
111
|
+
const byCategory = {};
|
|
112
|
+
for (const d of diagnostics) {
|
|
113
|
+
if (!byCategory[d.category]) byCategory[d.category] = [];
|
|
114
|
+
byCategory[d.category].push(d);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Sort categories by order
|
|
118
|
+
const sortedCategories = Object.keys(byCategory).sort((a, b) => {
|
|
119
|
+
const orderA = CATEGORY_META[a]?.order || 99;
|
|
120
|
+
const orderB = CATEGORY_META[b]?.order || 99;
|
|
121
|
+
return orderA - orderB;
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
lines.push('## Diagnostics');
|
|
125
|
+
lines.push('');
|
|
126
|
+
|
|
127
|
+
for (const category of sortedCategories) {
|
|
128
|
+
const meta = CATEGORY_META[category] || { name: category, icon: '📋' };
|
|
129
|
+
lines.push(`### ${meta.icon} ${meta.name}`);
|
|
130
|
+
lines.push('');
|
|
131
|
+
|
|
132
|
+
for (const d of byCategory[category]) {
|
|
133
|
+
const icon = {
|
|
134
|
+
[SEVERITY.PASS]: '✓',
|
|
135
|
+
[SEVERITY.INFO]: 'ℹ',
|
|
136
|
+
[SEVERITY.WARNING]: '⚠',
|
|
137
|
+
[SEVERITY.ERROR]: '✗',
|
|
138
|
+
[SEVERITY.CRITICAL]: '🔴',
|
|
139
|
+
}[d.severity] || '•';
|
|
140
|
+
|
|
141
|
+
lines.push(`- ${icon} **${d.name}**: ${d.message}`);
|
|
142
|
+
|
|
143
|
+
if (d.detail) {
|
|
144
|
+
lines.push(` - ${d.detail}`);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (d.fixes && d.fixes.length > 0) {
|
|
148
|
+
for (const fix of d.fixes) {
|
|
149
|
+
if (fix.command) {
|
|
150
|
+
lines.push(` - Fix: \`${fix.command}\``);
|
|
151
|
+
} else if (fix.url) {
|
|
152
|
+
lines.push(` - [${fix.description}](${fix.url})`);
|
|
153
|
+
} else if (fix.description) {
|
|
154
|
+
lines.push(` - Fix: ${fix.description}`);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
lines.push('');
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Issues section
|
|
163
|
+
const issues = diagnostics.filter(d =>
|
|
164
|
+
d.severity === SEVERITY.CRITICAL ||
|
|
165
|
+
d.severity === SEVERITY.ERROR ||
|
|
166
|
+
d.severity === SEVERITY.WARNING
|
|
167
|
+
);
|
|
168
|
+
|
|
169
|
+
if (issues.length > 0) {
|
|
170
|
+
lines.push('## Issues to Fix');
|
|
171
|
+
lines.push('');
|
|
172
|
+
|
|
173
|
+
const critical = issues.filter(d => d.severity === SEVERITY.CRITICAL);
|
|
174
|
+
const errors = issues.filter(d => d.severity === SEVERITY.ERROR);
|
|
175
|
+
const warnings = issues.filter(d => d.severity === SEVERITY.WARNING);
|
|
176
|
+
|
|
177
|
+
if (critical.length > 0) {
|
|
178
|
+
lines.push('### 🔴 Critical');
|
|
179
|
+
for (const d of critical) {
|
|
180
|
+
lines.push(`1. **${d.name}**: ${d.message}`);
|
|
181
|
+
if (d.fixes?.[0]?.command) {
|
|
182
|
+
lines.push(` \`\`\`bash`);
|
|
183
|
+
lines.push(` ${d.fixes[0].command}`);
|
|
184
|
+
lines.push(` \`\`\``);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
lines.push('');
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (errors.length > 0) {
|
|
191
|
+
lines.push('### ✗ Errors');
|
|
192
|
+
for (const d of errors) {
|
|
193
|
+
lines.push(`1. **${d.name}**: ${d.message}`);
|
|
194
|
+
if (d.fixes?.[0]?.command) {
|
|
195
|
+
lines.push(` \`\`\`bash`);
|
|
196
|
+
lines.push(` ${d.fixes[0].command}`);
|
|
197
|
+
lines.push(` \`\`\``);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
lines.push('');
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (warnings.length > 0) {
|
|
204
|
+
lines.push('### ⚠ Warnings');
|
|
205
|
+
for (const d of warnings) {
|
|
206
|
+
lines.push(`1. **${d.name}**: ${d.message}`);
|
|
207
|
+
if (d.fixes?.[0]?.command) {
|
|
208
|
+
lines.push(` \`\`\`bash`);
|
|
209
|
+
lines.push(` ${d.fixes[0].command}`);
|
|
210
|
+
lines.push(` \`\`\``);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
lines.push('');
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
lines.push('---');
|
|
218
|
+
lines.push(`*Generated by vibecheck doctor v${REPORT_VERSION}*`);
|
|
219
|
+
|
|
220
|
+
return lines.join('\n');
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function saveReports(diagnostics, projectPath, durationMs, outputDir) {
|
|
224
|
+
const jsonReport = generateJsonReport(diagnostics, projectPath, durationMs);
|
|
225
|
+
const mdReport = generateMarkdownReport(diagnostics, projectPath, durationMs);
|
|
226
|
+
|
|
227
|
+
// Ensure output directory exists
|
|
228
|
+
const doctorDir = path.join(outputDir, 'doctor');
|
|
229
|
+
fs.mkdirSync(doctorDir, { recursive: true });
|
|
230
|
+
|
|
231
|
+
// Save timestamped reports
|
|
232
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
|
|
233
|
+
const jsonPath = path.join(doctorDir, `doctor_${timestamp}.json`);
|
|
234
|
+
const mdPath = path.join(doctorDir, `doctor_${timestamp}.md`);
|
|
235
|
+
|
|
236
|
+
fs.writeFileSync(jsonPath, JSON.stringify(jsonReport, null, 2));
|
|
237
|
+
fs.writeFileSync(mdPath, mdReport);
|
|
238
|
+
|
|
239
|
+
// Save stable pointers
|
|
240
|
+
const lastJsonPath = path.join(doctorDir, 'last_doctor.json');
|
|
241
|
+
const lastMdPath = path.join(doctorDir, 'last_doctor.md');
|
|
242
|
+
|
|
243
|
+
fs.writeFileSync(lastJsonPath, JSON.stringify(jsonReport, null, 2));
|
|
244
|
+
fs.writeFileSync(lastMdPath, mdReport);
|
|
245
|
+
|
|
246
|
+
return {
|
|
247
|
+
json: jsonPath,
|
|
248
|
+
md: mdPath,
|
|
249
|
+
lastJson: lastJsonPath,
|
|
250
|
+
lastMd: lastMdPath,
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
module.exports = {
|
|
255
|
+
REPORT_VERSION,
|
|
256
|
+
calculateHealthScore,
|
|
257
|
+
getVerdict,
|
|
258
|
+
createSummary,
|
|
259
|
+
generateJsonReport,
|
|
260
|
+
generateMarkdownReport,
|
|
261
|
+
saveReports,
|
|
262
|
+
};
|