vibecheck-mcp-server 3.4.0 ā 3.5.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/index.js +190 -14
- package/package.json +1 -1
- package/tools-v3.js +397 -64
- package/tools.js +495 -0
- package/index-v1.js +0 -698
package/index.js
CHANGED
|
@@ -63,7 +63,7 @@ const __dirname = path.dirname(__filename);
|
|
|
63
63
|
// CENTRALIZED CONFIGURATION
|
|
64
64
|
// ============================================================================
|
|
65
65
|
const CONFIG = {
|
|
66
|
-
VERSION: "2.
|
|
66
|
+
VERSION: "2.2.0",
|
|
67
67
|
BIN_PATH: path.join(__dirname, "..", "bin", "vibecheck.js"),
|
|
68
68
|
OUTPUT_DIR: ".vibecheck",
|
|
69
69
|
ENV_DEFAULTS: { VIBECHECK_SKIP_AUTH: "1" },
|
|
@@ -901,29 +901,117 @@ class VibecheckMCP {
|
|
|
901
901
|
return '## Error: Invalid summary data\n';
|
|
902
902
|
}
|
|
903
903
|
|
|
904
|
-
// Safely extract values with defaults
|
|
905
|
-
const score = sanitizeNumber(
|
|
906
|
-
|
|
907
|
-
|
|
904
|
+
// Safely extract values with defaults - handle different output structures
|
|
905
|
+
const score = sanitizeNumber(
|
|
906
|
+
summary.score?.overall || summary.score || summary.verdict?.score || 0,
|
|
907
|
+
0, 100, 0
|
|
908
|
+
);
|
|
909
|
+
const grade = sanitizeString(summary.grade || summary.verdict?.grade, 10) || 'N/A';
|
|
910
|
+
const verdict = summary.verdict?.verdict || summary.verdict || (score >= 80 ? 'SHIP' : score >= 50 ? 'WARN' : 'BLOCK');
|
|
911
|
+
const canShip = verdict === 'SHIP' || Boolean(summary.canShip);
|
|
912
|
+
const findings = sanitizeArray(summary.findings || summary.report?.findings || [], 500);
|
|
908
913
|
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
914
|
+
// Get severity counts
|
|
915
|
+
const sevCounts = { critical: 0, high: 0, medium: 0, low: 0 };
|
|
916
|
+
for (const f of findings) {
|
|
917
|
+
const sev = (f.severity || 'medium').toLowerCase();
|
|
918
|
+
if (sev === 'block' || sev === 'critical') sevCounts.critical++;
|
|
919
|
+
else if (sev === 'high') sevCounts.high++;
|
|
920
|
+
else if (sev === 'warn' || sev === 'warning' || sev === 'medium') sevCounts.medium++;
|
|
921
|
+
else sevCounts.low++;
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
// Build structured output
|
|
925
|
+
let output = '';
|
|
926
|
+
|
|
927
|
+
// Header with verdict
|
|
928
|
+
const verdictEmoji = verdict === 'SHIP' ? 'ā
' : verdict === 'WARN' ? 'ā ļø' : 'š«';
|
|
929
|
+
output += `## ${verdictEmoji} VERDICT: ${verdict}\n\n`;
|
|
930
|
+
output += `**Health Score:** ${score}/100 (${grade})\n\n`;
|
|
931
|
+
|
|
932
|
+
// Summary stats
|
|
933
|
+
output += `### Summary\n`;
|
|
934
|
+
output += `- **Total Issues:** ${findings.length}\n`;
|
|
935
|
+
output += `- **Critical:** ${sevCounts.critical}\n`;
|
|
936
|
+
output += `- **High:** ${sevCounts.high}\n`;
|
|
937
|
+
output += `- **Medium:** ${sevCounts.medium}\n`;
|
|
938
|
+
output += `- **Low:** ${sevCounts.low}\n\n`;
|
|
939
|
+
|
|
940
|
+
// Category breakdown
|
|
912
941
|
if (summary.counts && typeof summary.counts === 'object') {
|
|
913
|
-
output += "###
|
|
914
|
-
output += "| Category |
|
|
942
|
+
output += "### Issue Categories\n\n";
|
|
943
|
+
output += "| Category | Count | Status |\n|----------|-------|--------|\n";
|
|
915
944
|
|
|
916
|
-
|
|
917
|
-
const entries = Object.entries(summary.counts).slice(0, 50);
|
|
945
|
+
const entries = Object.entries(summary.counts).slice(0, 20);
|
|
918
946
|
for (const [key, count] of entries) {
|
|
919
947
|
const safeKey = sanitizeString(key, 50);
|
|
920
948
|
const safeCount = sanitizeNumber(count, 0, 999999, 0);
|
|
921
949
|
const icon = safeCount === 0 ? "ā
" : "ā ļø";
|
|
922
|
-
output += `| ${
|
|
950
|
+
output += `| ${safeKey} | ${safeCount} | ${icon} |\n`;
|
|
951
|
+
}
|
|
952
|
+
output += '\n';
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
// Top findings with details (show up to 15)
|
|
956
|
+
if (findings.length > 0) {
|
|
957
|
+
output += "### Top Issues to Fix\n\n";
|
|
958
|
+
|
|
959
|
+
// Sort by severity (critical first)
|
|
960
|
+
const sortedFindings = findings
|
|
961
|
+
.sort((a, b) => {
|
|
962
|
+
const sevOrder = { critical: 0, block: 0, high: 1, warn: 2, warning: 2, medium: 2, low: 3, info: 4 };
|
|
963
|
+
const aOrder = sevOrder[(a.severity || 'medium').toLowerCase()] || 2;
|
|
964
|
+
const bOrder = sevOrder[(b.severity || 'medium').toLowerCase()] || 2;
|
|
965
|
+
return aOrder - bOrder;
|
|
966
|
+
})
|
|
967
|
+
.slice(0, 15);
|
|
968
|
+
|
|
969
|
+
for (let i = 0; i < sortedFindings.length; i++) {
|
|
970
|
+
const f = sortedFindings[i];
|
|
971
|
+
const sev = sanitizeString(f.severity || 'medium', 20).toUpperCase();
|
|
972
|
+
const sevEmoji = sev === 'CRITICAL' || sev === 'BLOCK' ? 'š“' : sev === 'HIGH' ? 'š ' : 'š”';
|
|
973
|
+
const title = sanitizeString(f.title || f.message || f.why || 'Unknown issue', 100);
|
|
974
|
+
const category = sanitizeString(f.category || f.type || 'Other', 30);
|
|
975
|
+
const file = sanitizeString(f.file || (f.evidence && f.evidence[0]?.file) || '', 150);
|
|
976
|
+
const line = f.line || (f.evidence && f.evidence[0]?.lines) || '';
|
|
977
|
+
|
|
978
|
+
output += `**${i + 1}. ${sevEmoji} [${sev}] ${title}**\n`;
|
|
979
|
+
output += ` - Category: \`${category}\`\n`;
|
|
980
|
+
if (file) {
|
|
981
|
+
output += ` - File: \`${file}\`${line ? `:${line}` : ''}\n`;
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
// Include fix hints if available
|
|
985
|
+
const fixHint = f.fixHints?.[0] || f.fixHint || f.fix;
|
|
986
|
+
if (fixHint) {
|
|
987
|
+
output += ` - **Fix:** ${sanitizeString(fixHint, 200)}\n`;
|
|
988
|
+
}
|
|
989
|
+
output += '\n';
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
if (findings.length > 15) {
|
|
993
|
+
output += `_...and ${findings.length - 15} more issues. See full report for details._\n\n`;
|
|
923
994
|
}
|
|
924
995
|
}
|
|
925
996
|
|
|
926
|
-
|
|
997
|
+
// Next steps based on verdict
|
|
998
|
+
output += "### Recommended Actions\n\n";
|
|
999
|
+
if (verdict === 'SHIP') {
|
|
1000
|
+
output += "- ā
Code is clean and ready to ship\n";
|
|
1001
|
+
output += "- Consider running `vibecheck ship --badge` to generate a status badge\n";
|
|
1002
|
+
} else if (verdict === 'WARN') {
|
|
1003
|
+
output += "- Review the warnings above before shipping\n";
|
|
1004
|
+
output += "- Run `vibecheck fix --plan` to see fix suggestions\n";
|
|
1005
|
+
output += "- Consider using `vibecheck fix --apply` (PRO) to auto-fix issues\n";
|
|
1006
|
+
} else {
|
|
1007
|
+
output += "- š« Critical issues must be fixed before shipping\n";
|
|
1008
|
+
output += "- Run `vibecheck fix --plan` to see detailed fix suggestions\n";
|
|
1009
|
+
output += "- Address critical issues first, then re-scan\n";
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
output += `\nš **Full Report:** ${CONFIG.OUTPUT_DIR}/report.html\n`;
|
|
1013
|
+
output += `š **JSON Results:** ${CONFIG.OUTPUT_DIR}/results/latest.json\n`;
|
|
1014
|
+
|
|
927
1015
|
return output;
|
|
928
1016
|
}
|
|
929
1017
|
|
|
@@ -1196,6 +1284,12 @@ class VibecheckMCP {
|
|
|
1196
1284
|
// Resources
|
|
1197
1285
|
this.server.setRequestHandler(ListResourcesRequestSchema, async () => ({
|
|
1198
1286
|
resources: [
|
|
1287
|
+
{
|
|
1288
|
+
uri: "vibecheck://status",
|
|
1289
|
+
name: "Server Status",
|
|
1290
|
+
description: "Current MCP server health, version, and rate limits",
|
|
1291
|
+
mimeType: "application/json",
|
|
1292
|
+
},
|
|
1199
1293
|
{
|
|
1200
1294
|
uri: "vibecheck://config",
|
|
1201
1295
|
name: "vibecheck Config",
|
|
@@ -1244,6 +1338,12 @@ class VibecheckMCP {
|
|
|
1244
1338
|
description: "Last prove loop results (ctx ā reality ā ship ā fix)",
|
|
1245
1339
|
mimeType: "application/json",
|
|
1246
1340
|
},
|
|
1341
|
+
{
|
|
1342
|
+
uri: "vibecheck://registry",
|
|
1343
|
+
name: "Command Registry",
|
|
1344
|
+
description: "Available commands and their tiers",
|
|
1345
|
+
mimeType: "application/json",
|
|
1346
|
+
},
|
|
1247
1347
|
],
|
|
1248
1348
|
}));
|
|
1249
1349
|
|
|
@@ -1390,6 +1490,82 @@ class VibecheckMCP {
|
|
|
1390
1490
|
return await safeReadResource(provePath, "No prove results. Run vibecheck.prove first.");
|
|
1391
1491
|
}
|
|
1392
1492
|
|
|
1493
|
+
// Server status resource - live health check
|
|
1494
|
+
if (uri === "vibecheck://status") {
|
|
1495
|
+
const rateCheck = checkRateLimit(null);
|
|
1496
|
+
const circuitCheck = checkCircuitBreaker();
|
|
1497
|
+
|
|
1498
|
+
const status = {
|
|
1499
|
+
server: {
|
|
1500
|
+
version: CONFIG.VERSION,
|
|
1501
|
+
uptime: Math.floor(process.uptime()),
|
|
1502
|
+
transport: 'stdio',
|
|
1503
|
+
nodeVersion: process.version,
|
|
1504
|
+
},
|
|
1505
|
+
health: {
|
|
1506
|
+
status: circuitCheck.allowed ? 'healthy' : 'degraded',
|
|
1507
|
+
circuitBreaker: circuitBreakerState.state,
|
|
1508
|
+
failureCount: circuitBreakerState.failures,
|
|
1509
|
+
},
|
|
1510
|
+
rateLimit: {
|
|
1511
|
+
remaining: rateCheck.remaining,
|
|
1512
|
+
resetIn: rateCheck.resetIn || 0,
|
|
1513
|
+
limit: CONFIG.LIMITS.RATE_LIMIT_MAX_CALLS,
|
|
1514
|
+
window: CONFIG.LIMITS.RATE_LIMIT_WINDOW_MS,
|
|
1515
|
+
},
|
|
1516
|
+
features: {
|
|
1517
|
+
hardening: true,
|
|
1518
|
+
auditLogging: true,
|
|
1519
|
+
secretRedaction: true,
|
|
1520
|
+
pathSecurity: true,
|
|
1521
|
+
},
|
|
1522
|
+
timestamp: new Date().toISOString(),
|
|
1523
|
+
};
|
|
1524
|
+
|
|
1525
|
+
return {
|
|
1526
|
+
contents: [{ uri, mimeType: "application/json", text: JSON.stringify(status, null, 2) }],
|
|
1527
|
+
};
|
|
1528
|
+
}
|
|
1529
|
+
|
|
1530
|
+
// Registry resource - available commands and tiers
|
|
1531
|
+
if (uri === "vibecheck://registry") {
|
|
1532
|
+
const registry = {
|
|
1533
|
+
version: CONFIG.VERSION,
|
|
1534
|
+
tiers: {
|
|
1535
|
+
free: {
|
|
1536
|
+
name: 'FREE',
|
|
1537
|
+
price: '$0',
|
|
1538
|
+
description: 'Inspect & Observe',
|
|
1539
|
+
},
|
|
1540
|
+
pro: {
|
|
1541
|
+
name: 'PRO',
|
|
1542
|
+
price: '$69/mo',
|
|
1543
|
+
description: 'Fix, Prove & Enforce',
|
|
1544
|
+
},
|
|
1545
|
+
},
|
|
1546
|
+
tools: Object.entries(V3_TOOL_TIERS).map(([name, tier]) => ({
|
|
1547
|
+
name,
|
|
1548
|
+
tier,
|
|
1549
|
+
})),
|
|
1550
|
+
resources: [
|
|
1551
|
+
'vibecheck://status',
|
|
1552
|
+
'vibecheck://config',
|
|
1553
|
+
'vibecheck://summary',
|
|
1554
|
+
'vibecheck://truthpack',
|
|
1555
|
+
'vibecheck://missions',
|
|
1556
|
+
'vibecheck://reality',
|
|
1557
|
+
'vibecheck://findings',
|
|
1558
|
+
'vibecheck://share',
|
|
1559
|
+
'vibecheck://prove',
|
|
1560
|
+
'vibecheck://registry',
|
|
1561
|
+
],
|
|
1562
|
+
};
|
|
1563
|
+
|
|
1564
|
+
return {
|
|
1565
|
+
contents: [{ uri, mimeType: "application/json", text: JSON.stringify(registry, null, 2) }],
|
|
1566
|
+
};
|
|
1567
|
+
}
|
|
1568
|
+
|
|
1393
1569
|
return {
|
|
1394
1570
|
contents: [{
|
|
1395
1571
|
uri,
|