@vibecheckai/cli 3.5.0 → 3.5.2
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/registry.js +214 -237
- package/bin/runners/cli-utils.js +33 -2
- package/bin/runners/context/analyzer.js +52 -1
- package/bin/runners/context/generators/cursor.js +2 -49
- package/bin/runners/context/git-context.js +3 -1
- package/bin/runners/context/team-conventions.js +33 -7
- package/bin/runners/lib/analysis-core.js +25 -5
- package/bin/runners/lib/analyzers.js +431 -481
- package/bin/runners/lib/default-config.js +127 -0
- package/bin/runners/lib/doctor/modules/security.js +3 -1
- package/bin/runners/lib/engine/ast-cache.js +210 -0
- package/bin/runners/lib/engine/auth-extractor.js +211 -0
- package/bin/runners/lib/engine/billing-extractor.js +112 -0
- package/bin/runners/lib/engine/enforcement-extractor.js +100 -0
- package/bin/runners/lib/engine/env-extractor.js +207 -0
- package/bin/runners/lib/engine/express-extractor.js +208 -0
- package/bin/runners/lib/engine/extractors.js +849 -0
- package/bin/runners/lib/engine/index.js +207 -0
- package/bin/runners/lib/engine/repo-index.js +514 -0
- package/bin/runners/lib/engine/types.js +124 -0
- package/bin/runners/lib/engines/accessibility-engine.js +18 -218
- package/bin/runners/lib/engines/api-consistency-engine.js +30 -335
- package/bin/runners/lib/engines/cross-file-analysis-engine.js +27 -292
- package/bin/runners/lib/engines/empty-catch-engine.js +17 -127
- package/bin/runners/lib/engines/mock-data-engine.js +10 -53
- package/bin/runners/lib/engines/performance-issues-engine.js +36 -176
- package/bin/runners/lib/engines/security-vulnerabilities-engine.js +54 -382
- package/bin/runners/lib/engines/type-aware-engine.js +39 -263
- package/bin/runners/lib/engines/vibecheck-engines/index.js +13 -122
- 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 +73 -373
- 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/entitlements-v2.js +73 -97
- package/bin/runners/lib/error-handler.js +44 -3
- package/bin/runners/lib/error-messages.js +289 -0
- package/bin/runners/lib/evidence-pack.js +7 -1
- package/bin/runners/lib/finding-id.js +69 -0
- package/bin/runners/lib/finding-sorter.js +89 -0
- package/bin/runners/lib/html-proof-report.js +700 -350
- package/bin/runners/lib/missions/plan.js +6 -46
- package/bin/runners/lib/missions/templates.js +0 -232
- package/bin/runners/lib/next-action.js +560 -0
- package/bin/runners/lib/prerequisites.js +149 -0
- package/bin/runners/lib/route-detection.js +137 -68
- package/bin/runners/lib/scan-output.js +91 -76
- package/bin/runners/lib/scan-runner.js +135 -0
- package/bin/runners/lib/schemas/ajv-validator.js +464 -0
- package/bin/runners/lib/schemas/error-envelope.schema.json +105 -0
- package/bin/runners/lib/schemas/finding-v3.schema.json +151 -0
- package/bin/runners/lib/schemas/report-artifact.schema.json +120 -0
- package/bin/runners/lib/schemas/run-request.schema.json +108 -0
- package/bin/runners/lib/schemas/validator.js +27 -0
- package/bin/runners/lib/schemas/verdict.schema.json +140 -0
- package/bin/runners/lib/ship-output-enterprise.js +23 -23
- package/bin/runners/lib/ship-output.js +75 -31
- package/bin/runners/lib/terminal-ui.js +6 -113
- package/bin/runners/lib/truth.js +351 -10
- package/bin/runners/lib/unified-cli-output.js +430 -603
- package/bin/runners/lib/unified-output.js +13 -9
- package/bin/runners/runAIAgent.js +10 -5
- package/bin/runners/runAgent.js +0 -3
- package/bin/runners/runAllowlist.js +389 -0
- package/bin/runners/runApprove.js +0 -33
- package/bin/runners/runAuth.js +73 -45
- package/bin/runners/runCheckpoint.js +51 -11
- package/bin/runners/runClassify.js +85 -21
- package/bin/runners/runContext.js +0 -3
- package/bin/runners/runDoctor.js +41 -28
- package/bin/runners/runEvidencePack.js +362 -0
- package/bin/runners/runFirewall.js +0 -3
- package/bin/runners/runFirewallHook.js +0 -3
- package/bin/runners/runFix.js +66 -76
- package/bin/runners/runGuard.js +18 -411
- package/bin/runners/runInit.js +113 -30
- package/bin/runners/runLabs.js +424 -0
- package/bin/runners/runMcp.js +19 -25
- package/bin/runners/runPolish.js +64 -240
- package/bin/runners/runPromptFirewall.js +12 -5
- package/bin/runners/runProve.js +57 -22
- package/bin/runners/runQuickstart.js +531 -0
- package/bin/runners/runReality.js +59 -68
- package/bin/runners/runReport.js +38 -33
- package/bin/runners/runRuntime.js +8 -5
- package/bin/runners/runScan.js +1413 -190
- package/bin/runners/runShip.js +113 -719
- package/bin/runners/runTruth.js +0 -3
- package/bin/runners/runValidate.js +13 -9
- package/bin/runners/runWatch.js +23 -14
- package/bin/scan.js +6 -1
- package/bin/vibecheck.js +204 -185
- package/mcp-server/deprecation-middleware.js +282 -0
- package/mcp-server/handlers/index.ts +15 -0
- package/mcp-server/handlers/tool-handler.ts +554 -0
- package/mcp-server/index-v1.js +698 -0
- package/mcp-server/index.js +210 -238
- package/mcp-server/lib/cache-wrapper.cjs +383 -0
- package/mcp-server/lib/error-envelope.js +138 -0
- package/mcp-server/lib/executor.ts +499 -0
- package/mcp-server/lib/index.ts +19 -0
- package/mcp-server/lib/rate-limiter.js +166 -0
- package/mcp-server/lib/sandbox.test.ts +519 -0
- package/mcp-server/lib/sandbox.ts +395 -0
- package/mcp-server/lib/types.ts +267 -0
- package/mcp-server/package.json +12 -3
- package/mcp-server/registry/tool-registry.js +794 -0
- package/mcp-server/registry/tools.json +605 -0
- package/mcp-server/registry.test.ts +334 -0
- package/mcp-server/tests/tier-gating.test.js +297 -0
- package/mcp-server/tier-auth.js +378 -45
- package/mcp-server/tools-v3.js +353 -442
- package/mcp-server/tsconfig.json +37 -0
- package/mcp-server/vibecheck-2.0-tools.js +14 -1
- package/package.json +1 -1
- package/bin/runners/lib/agent-firewall/learning/learning-engine.js +0 -849
- package/bin/runners/lib/audit-logger.js +0 -532
- package/bin/runners/lib/authority/authorities/architecture.js +0 -364
- package/bin/runners/lib/authority/authorities/compliance.js +0 -341
- package/bin/runners/lib/authority/authorities/human.js +0 -343
- package/bin/runners/lib/authority/authorities/quality.js +0 -420
- package/bin/runners/lib/authority/authorities/security.js +0 -228
- package/bin/runners/lib/authority/index.js +0 -293
- package/bin/runners/lib/bundle/bundle-intelligence.js +0 -846
- package/bin/runners/lib/cli-charts.js +0 -368
- package/bin/runners/lib/cli-config-display.js +0 -405
- package/bin/runners/lib/cli-demo.js +0 -275
- package/bin/runners/lib/cli-errors.js +0 -438
- package/bin/runners/lib/cli-help-formatter.js +0 -439
- package/bin/runners/lib/cli-interactive-menu.js +0 -509
- package/bin/runners/lib/cli-prompts.js +0 -441
- package/bin/runners/lib/cli-scan-cards.js +0 -362
- package/bin/runners/lib/compliance-reporter.js +0 -710
- package/bin/runners/lib/conductor/index.js +0 -671
- package/bin/runners/lib/easy/README.md +0 -123
- package/bin/runners/lib/easy/index.js +0 -140
- package/bin/runners/lib/easy/interactive-wizard.js +0 -788
- package/bin/runners/lib/easy/one-click-firewall.js +0 -564
- package/bin/runners/lib/easy/zero-config-reality.js +0 -714
- package/bin/runners/lib/engines/async-patterns-engine.js +0 -444
- package/bin/runners/lib/engines/bundle-size-engine.js +0 -433
- package/bin/runners/lib/engines/confidence-scoring.js +0 -276
- package/bin/runners/lib/engines/context-detection.js +0 -264
- package/bin/runners/lib/engines/database-patterns-engine.js +0 -429
- package/bin/runners/lib/engines/duplicate-code-engine.js +0 -354
- package/bin/runners/lib/engines/env-variables-engine.js +0 -458
- package/bin/runners/lib/engines/error-handling-engine.js +0 -437
- package/bin/runners/lib/engines/false-positive-prevention.js +0 -630
- package/bin/runners/lib/engines/framework-adapters/index.js +0 -607
- package/bin/runners/lib/engines/framework-detection.js +0 -508
- package/bin/runners/lib/engines/import-order-engine.js +0 -429
- package/bin/runners/lib/engines/naming-conventions-engine.js +0 -544
- package/bin/runners/lib/engines/noise-reduction-engine.js +0 -452
- package/bin/runners/lib/engines/orchestrator.js +0 -334
- package/bin/runners/lib/engines/react-patterns-engine.js +0 -457
- package/bin/runners/lib/engines/vibecheck-engines/lib/ai-hallucination-engine.js +0 -806
- package/bin/runners/lib/engines/vibecheck-engines/lib/smart-fix-engine.js +0 -577
- package/bin/runners/lib/engines/vibecheck-engines/lib/vibe-score-engine.js +0 -543
- package/bin/runners/lib/engines/vibecheck-engines.js +0 -514
- package/bin/runners/lib/enhanced-features/index.js +0 -305
- package/bin/runners/lib/enhanced-output.js +0 -631
- package/bin/runners/lib/enterprise.js +0 -300
- package/bin/runners/lib/firewall/command-validator.js +0 -351
- package/bin/runners/lib/firewall/config.js +0 -341
- package/bin/runners/lib/firewall/content-validator.js +0 -519
- package/bin/runners/lib/firewall/index.js +0 -101
- package/bin/runners/lib/firewall/path-validator.js +0 -256
- package/bin/runners/lib/intelligence/cross-repo-intelligence.js +0 -817
- package/bin/runners/lib/mcp-utils.js +0 -425
- package/bin/runners/lib/output/index.js +0 -1022
- package/bin/runners/lib/policy-engine.js +0 -652
- package/bin/runners/lib/polish/autofix/accessibility-fixes.js +0 -333
- package/bin/runners/lib/polish/autofix/async-handlers.js +0 -273
- package/bin/runners/lib/polish/autofix/dead-code.js +0 -280
- package/bin/runners/lib/polish/autofix/imports-optimizer.js +0 -344
- package/bin/runners/lib/polish/autofix/index.js +0 -200
- package/bin/runners/lib/polish/autofix/remove-consoles.js +0 -209
- package/bin/runners/lib/polish/autofix/strengthen-types.js +0 -245
- package/bin/runners/lib/polish/backend-checks.js +0 -148
- package/bin/runners/lib/polish/documentation-checks.js +0 -111
- package/bin/runners/lib/polish/frontend-checks.js +0 -168
- package/bin/runners/lib/polish/index.js +0 -71
- package/bin/runners/lib/polish/infrastructure-checks.js +0 -131
- package/bin/runners/lib/polish/library-detection.js +0 -175
- package/bin/runners/lib/polish/performance-checks.js +0 -100
- package/bin/runners/lib/polish/security-checks.js +0 -148
- package/bin/runners/lib/polish/utils.js +0 -203
- package/bin/runners/lib/prompt-builder.js +0 -540
- package/bin/runners/lib/proof-certificate.js +0 -634
- package/bin/runners/lib/reality/accessibility-audit.js +0 -946
- package/bin/runners/lib/reality/api-contract-validator.js +0 -1012
- package/bin/runners/lib/reality/chaos-engineering.js +0 -1084
- package/bin/runners/lib/reality/performance-tracker.js +0 -1077
- package/bin/runners/lib/reality/scenario-generator.js +0 -1404
- package/bin/runners/lib/reality/visual-regression.js +0 -852
- package/bin/runners/lib/reality-profiler.js +0 -717
- package/bin/runners/lib/replay/flight-recorder-viewer.js +0 -1160
- package/bin/runners/lib/review/ai-code-review.js +0 -832
- package/bin/runners/lib/rules/custom-rule-engine.js +0 -985
- package/bin/runners/lib/sbom-generator.js +0 -641
- package/bin/runners/lib/scan-output-enhanced.js +0 -512
- package/bin/runners/lib/security/owasp-scanner.js +0 -939
- package/bin/runners/lib/validators/contract-validator.js +0 -283
- package/bin/runners/lib/validators/dead-export-detector.js +0 -279
- package/bin/runners/lib/validators/dep-audit.js +0 -245
- package/bin/runners/lib/validators/env-validator.js +0 -319
- package/bin/runners/lib/validators/index.js +0 -120
- package/bin/runners/lib/validators/license-checker.js +0 -252
- package/bin/runners/lib/validators/route-validator.js +0 -290
- package/bin/runners/runAuthority.js +0 -528
- package/bin/runners/runConductor.js +0 -772
- package/bin/runners/runContainer.js +0 -366
- package/bin/runners/runEasy.js +0 -410
- package/bin/runners/runIaC.js +0 -372
- package/bin/runners/runVibe.js +0 -791
- package/mcp-server/tools.js +0 -495
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cache Wrapper for MCP Server (CommonJS)
|
|
3
|
+
*
|
|
4
|
+
* Provides cache integration for vibecheck MCP tools.
|
|
5
|
+
* Wraps tool execution with caching and returns cacheStats.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const crypto = require('crypto');
|
|
11
|
+
const os = require('os');
|
|
12
|
+
|
|
13
|
+
// =============================================================================
|
|
14
|
+
// CONFIGURATION
|
|
15
|
+
// =============================================================================
|
|
16
|
+
|
|
17
|
+
const CACHE_VERSION = '1.0.0';
|
|
18
|
+
const CACHE_MAX_AGE_MS = 24 * 60 * 60 * 1000; // 24 hours
|
|
19
|
+
const CACHE_MAX_ENTRIES = 100;
|
|
20
|
+
const SCHEMA_VERSION = '1.0.0';
|
|
21
|
+
|
|
22
|
+
const CONFIG_FILES = ['.vibecheckrc.json', '.vibecheckrc', 'vibecheck.config.js'];
|
|
23
|
+
const LOCKFILES = ['pnpm-lock.yaml', 'package-lock.json', 'yarn.lock', 'bun.lockb'];
|
|
24
|
+
|
|
25
|
+
// =============================================================================
|
|
26
|
+
// CACHE UTILITIES
|
|
27
|
+
// =============================================================================
|
|
28
|
+
|
|
29
|
+
function getCacheDir() {
|
|
30
|
+
const home = os.homedir();
|
|
31
|
+
if (process.platform === 'win32') {
|
|
32
|
+
return path.join(
|
|
33
|
+
process.env.APPDATA || path.join(home, 'AppData', 'Roaming'),
|
|
34
|
+
'vibecheck',
|
|
35
|
+
'cache',
|
|
36
|
+
'runs'
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
return path.join(home, '.cache', 'vibecheck', 'runs');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function getCacheFilePath(projectPath) {
|
|
43
|
+
const cacheDir = getCacheDir();
|
|
44
|
+
const projectHash = crypto
|
|
45
|
+
.createHash('sha256')
|
|
46
|
+
.update(path.resolve(projectPath))
|
|
47
|
+
.digest('hex')
|
|
48
|
+
.slice(0, 16);
|
|
49
|
+
return path.join(cacheDir, `tool-cache-${projectHash}.json`);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function ensureCacheDir() {
|
|
53
|
+
const cacheDir = getCacheDir();
|
|
54
|
+
if (!fs.existsSync(cacheDir)) {
|
|
55
|
+
fs.mkdirSync(cacheDir, { recursive: true, mode: 0o700 });
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function computeFileHash(filePath) {
|
|
60
|
+
try {
|
|
61
|
+
if (!fs.existsSync(filePath)) return null;
|
|
62
|
+
const content = fs.readFileSync(filePath);
|
|
63
|
+
return crypto.createHash('sha256').update(content).digest('hex').slice(0, 16);
|
|
64
|
+
} catch {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function hashString(str) {
|
|
70
|
+
return crypto.createHash('sha256').update(str).digest('hex').slice(0, 16);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function computeRepoFingerprint(projectPath) {
|
|
74
|
+
const components = [];
|
|
75
|
+
|
|
76
|
+
// Hash config files
|
|
77
|
+
for (const configFile of CONFIG_FILES) {
|
|
78
|
+
const configPath = path.join(projectPath, configFile);
|
|
79
|
+
const hash = computeFileHash(configPath);
|
|
80
|
+
if (hash) {
|
|
81
|
+
components.push(`config:${configFile}:${hash}`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Hash lockfiles
|
|
86
|
+
for (const lockfile of LOCKFILES) {
|
|
87
|
+
const lockPath = path.join(projectPath, lockfile);
|
|
88
|
+
const hash = computeFileHash(lockPath);
|
|
89
|
+
if (hash) {
|
|
90
|
+
components.push(`lock:${lockfile}:${hash}`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Hash root package.json
|
|
95
|
+
const manifestPath = path.join(projectPath, 'package.json');
|
|
96
|
+
const manifestHash = computeFileHash(manifestPath);
|
|
97
|
+
if (manifestHash) {
|
|
98
|
+
components.push(`manifest:root:${manifestHash}`);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const combined = components.sort().join('|');
|
|
102
|
+
return hashString(combined);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function generateCacheKey(toolName, projectPath, vibecheckVersion) {
|
|
106
|
+
const repoFingerprint = computeRepoFingerprint(projectPath);
|
|
107
|
+
return {
|
|
108
|
+
toolName,
|
|
109
|
+
vibecheckVersion,
|
|
110
|
+
schemaVersion: SCHEMA_VERSION,
|
|
111
|
+
repoFingerprint,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function cacheKeyToString(key) {
|
|
116
|
+
return `${key.toolName}:${key.vibecheckVersion}:${key.schemaVersion}:${key.repoFingerprint}`;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function loadCacheStore(projectPath) {
|
|
120
|
+
try {
|
|
121
|
+
const cachePath = getCacheFilePath(projectPath);
|
|
122
|
+
if (!fs.existsSync(cachePath)) {
|
|
123
|
+
return { version: CACHE_VERSION, entries: {} };
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const data = JSON.parse(fs.readFileSync(cachePath, 'utf8'));
|
|
127
|
+
|
|
128
|
+
if (data.version !== CACHE_VERSION) {
|
|
129
|
+
return { version: CACHE_VERSION, entries: {} };
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Prune old entries
|
|
133
|
+
const now = Date.now();
|
|
134
|
+
const entries = {};
|
|
135
|
+
for (const [keyStr, entry] of Object.entries(data.entries)) {
|
|
136
|
+
const createdAt = new Date(entry.createdAt).getTime();
|
|
137
|
+
if (now - createdAt < CACHE_MAX_AGE_MS) {
|
|
138
|
+
entries[keyStr] = entry;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return { version: CACHE_VERSION, entries };
|
|
143
|
+
} catch {
|
|
144
|
+
return { version: CACHE_VERSION, entries: {} };
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function saveCacheStore(projectPath, store) {
|
|
149
|
+
try {
|
|
150
|
+
ensureCacheDir();
|
|
151
|
+
const cachePath = getCacheFilePath(projectPath);
|
|
152
|
+
const tempPath = `${cachePath}.tmp.${Date.now()}`;
|
|
153
|
+
|
|
154
|
+
// Limit cache size
|
|
155
|
+
const entries = Object.entries(store.entries);
|
|
156
|
+
if (entries.length > CACHE_MAX_ENTRIES) {
|
|
157
|
+
entries.sort((a, b) => {
|
|
158
|
+
const aTime = new Date(a[1].createdAt).getTime();
|
|
159
|
+
const bTime = new Date(b[1].createdAt).getTime();
|
|
160
|
+
return bTime - aTime;
|
|
161
|
+
});
|
|
162
|
+
store.entries = Object.fromEntries(entries.slice(0, CACHE_MAX_ENTRIES));
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
fs.writeFileSync(tempPath, JSON.stringify(store, null, 2), { mode: 0o600 });
|
|
166
|
+
fs.renameSync(tempPath, cachePath);
|
|
167
|
+
} catch (err) {
|
|
168
|
+
if (process.env.DEBUG || process.env.VIBECHECK_DEBUG) {
|
|
169
|
+
console.warn(`Cache save failed: ${err.message}`);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function buildFileHashMap(projectPath, files) {
|
|
175
|
+
const hashMap = {};
|
|
176
|
+
for (const file of files) {
|
|
177
|
+
const fullPath = path.isAbsolute(file) ? file : path.join(projectPath, file);
|
|
178
|
+
const hash = computeFileHash(fullPath);
|
|
179
|
+
if (hash) {
|
|
180
|
+
const relPath = path.relative(projectPath, fullPath).replace(/\\/g, '/');
|
|
181
|
+
hashMap[relPath] = hash;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return hashMap;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// =============================================================================
|
|
188
|
+
// CACHE CLASS
|
|
189
|
+
// =============================================================================
|
|
190
|
+
|
|
191
|
+
class ToolCache {
|
|
192
|
+
constructor(projectPath, options = {}) {
|
|
193
|
+
this.projectPath = path.resolve(projectPath);
|
|
194
|
+
this.vibecheckVersion = options.vibecheckVersion || '3.3.0';
|
|
195
|
+
this.enabled = options.enabled !== false;
|
|
196
|
+
this.store = this.enabled ? loadCacheStore(this.projectPath) : { version: CACHE_VERSION, entries: {} };
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
get(toolName) {
|
|
200
|
+
if (!this.enabled) return null;
|
|
201
|
+
|
|
202
|
+
const key = generateCacheKey(toolName, this.projectPath, this.vibecheckVersion);
|
|
203
|
+
const keyStr = cacheKeyToString(key);
|
|
204
|
+
const entry = this.store.entries[keyStr];
|
|
205
|
+
|
|
206
|
+
if (entry) {
|
|
207
|
+
// Verify fingerprint still matches
|
|
208
|
+
const currentFingerprint = computeRepoFingerprint(this.projectPath);
|
|
209
|
+
if (currentFingerprint !== key.repoFingerprint) {
|
|
210
|
+
delete this.store.entries[keyStr];
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return {
|
|
215
|
+
entry,
|
|
216
|
+
stats: {
|
|
217
|
+
hit: true,
|
|
218
|
+
reusedFindingsCount: entry.output.findings?.length || 0,
|
|
219
|
+
durationMs: 0,
|
|
220
|
+
},
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return null;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
set(toolName, output, fileHashMap, durationMs) {
|
|
228
|
+
if (!this.enabled) return;
|
|
229
|
+
|
|
230
|
+
const key = generateCacheKey(toolName, this.projectPath, this.vibecheckVersion);
|
|
231
|
+
const keyStr = cacheKeyToString(key);
|
|
232
|
+
|
|
233
|
+
const entry = {
|
|
234
|
+
key,
|
|
235
|
+
output,
|
|
236
|
+
fileHashMap,
|
|
237
|
+
createdAt: new Date().toISOString(),
|
|
238
|
+
durationMs,
|
|
239
|
+
vibecheckVersion: this.vibecheckVersion,
|
|
240
|
+
schemaVersion: SCHEMA_VERSION,
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
this.store.entries[keyStr] = entry;
|
|
244
|
+
saveCacheStore(this.projectPath, this.store);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
invalidate(toolName) {
|
|
248
|
+
if (!this.enabled) return;
|
|
249
|
+
|
|
250
|
+
const key = generateCacheKey(toolName, this.projectPath, this.vibecheckVersion);
|
|
251
|
+
const keyStr = cacheKeyToString(key);
|
|
252
|
+
delete this.store.entries[keyStr];
|
|
253
|
+
saveCacheStore(this.projectPath, this.store);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
invalidateAll() {
|
|
257
|
+
if (!this.enabled) return;
|
|
258
|
+
this.store.entries = {};
|
|
259
|
+
saveCacheStore(this.projectPath, this.store);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// =============================================================================
|
|
264
|
+
// EXECUTE WITH CACHE
|
|
265
|
+
// =============================================================================
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Execute a tool with caching support
|
|
269
|
+
* @param {Object} options - Tool options
|
|
270
|
+
* @param {Function} executor - Tool executor function
|
|
271
|
+
* @returns {Promise<Object>} Result with cacheStats
|
|
272
|
+
*/
|
|
273
|
+
async function executeWithCache(options, executor) {
|
|
274
|
+
const startTime = Date.now();
|
|
275
|
+
const {
|
|
276
|
+
projectPath,
|
|
277
|
+
toolName,
|
|
278
|
+
useCache = true,
|
|
279
|
+
forceRefresh = false,
|
|
280
|
+
files = [],
|
|
281
|
+
vibecheckVersion = '3.3.0',
|
|
282
|
+
} = options;
|
|
283
|
+
|
|
284
|
+
const cache = new ToolCache(projectPath, {
|
|
285
|
+
vibecheckVersion,
|
|
286
|
+
enabled: useCache,
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
// Check for cache hit
|
|
290
|
+
if (!forceRefresh) {
|
|
291
|
+
const cached = cache.get(toolName);
|
|
292
|
+
if (cached) {
|
|
293
|
+
return {
|
|
294
|
+
...cached.entry.output,
|
|
295
|
+
cacheStats: {
|
|
296
|
+
hit: true,
|
|
297
|
+
reusedFindingsCount: cached.entry.output.findings?.length || 0,
|
|
298
|
+
durationMs: Date.now() - startTime,
|
|
299
|
+
},
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Cache miss - execute tool
|
|
305
|
+
const result = await executor(options);
|
|
306
|
+
const durationMs = Date.now() - startTime;
|
|
307
|
+
|
|
308
|
+
// Build file hash map
|
|
309
|
+
const fileHashMap = files.length > 0 ? buildFileHashMap(projectPath, files) : {};
|
|
310
|
+
|
|
311
|
+
// Store in cache
|
|
312
|
+
const output = {
|
|
313
|
+
findings: result.findings || [],
|
|
314
|
+
verdict: result.verdict || null,
|
|
315
|
+
metadata: {
|
|
316
|
+
...result.metadata,
|
|
317
|
+
cachedAt: new Date().toISOString(),
|
|
318
|
+
},
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
cache.set(toolName, output, fileHashMap, durationMs);
|
|
322
|
+
|
|
323
|
+
return {
|
|
324
|
+
...result,
|
|
325
|
+
cacheStats: {
|
|
326
|
+
hit: false,
|
|
327
|
+
reusedFindingsCount: 0,
|
|
328
|
+
durationMs,
|
|
329
|
+
},
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Wrap CLI command execution with caching
|
|
335
|
+
* @param {string} projectPath - Project path
|
|
336
|
+
* @param {string} toolName - Tool name
|
|
337
|
+
* @param {Function} cliExecutor - CLI executor function
|
|
338
|
+
* @param {Object} options - Additional options
|
|
339
|
+
* @returns {Promise<Object>} Result with cacheStats
|
|
340
|
+
*/
|
|
341
|
+
async function executeCachedCliCommand(projectPath, toolName, cliExecutor, options = {}) {
|
|
342
|
+
return executeWithCache(
|
|
343
|
+
{
|
|
344
|
+
projectPath,
|
|
345
|
+
toolName,
|
|
346
|
+
useCache: options.useCache !== false,
|
|
347
|
+
forceRefresh: options.forceRefresh || false,
|
|
348
|
+
files: options.files || [],
|
|
349
|
+
vibecheckVersion: options.vibecheckVersion || '3.3.0',
|
|
350
|
+
},
|
|
351
|
+
async () => cliExecutor()
|
|
352
|
+
);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// =============================================================================
|
|
356
|
+
// EXPORTS
|
|
357
|
+
// =============================================================================
|
|
358
|
+
|
|
359
|
+
module.exports = {
|
|
360
|
+
ToolCache,
|
|
361
|
+
executeWithCache,
|
|
362
|
+
executeCachedCliCommand,
|
|
363
|
+
computeFileHash,
|
|
364
|
+
computeRepoFingerprint,
|
|
365
|
+
buildFileHashMap,
|
|
366
|
+
getCacheDir,
|
|
367
|
+
clearAllCaches: () => {
|
|
368
|
+
try {
|
|
369
|
+
const cacheDir = getCacheDir();
|
|
370
|
+
if (fs.existsSync(cacheDir)) {
|
|
371
|
+
const files = fs.readdirSync(cacheDir);
|
|
372
|
+
for (const file of files) {
|
|
373
|
+
if (file.startsWith('tool-cache-') && file.endsWith('.json')) {
|
|
374
|
+
fs.unlinkSync(path.join(cacheDir, file));
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
return { success: true, message: 'All tool caches cleared' };
|
|
379
|
+
} catch (err) {
|
|
380
|
+
return { success: false, message: `Failed to clear caches: ${err.message}` };
|
|
381
|
+
}
|
|
382
|
+
},
|
|
383
|
+
};
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Error Envelope - consistent error format across all tools
|
|
3
|
+
*
|
|
4
|
+
* Provides standardized error and success envelopes for all MCP tool responses.
|
|
5
|
+
* Ensures consistent error handling, timeout support, and cancellation.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const ERROR_CODES = {
|
|
9
|
+
// Auth errors
|
|
10
|
+
AUTH_REQUIRED: { status: 401, message: 'Authentication required' },
|
|
11
|
+
AUTH_FAILED: { status: 401, message: 'Authentication failed' },
|
|
12
|
+
TIER_REQUIRED: { status: 403, message: 'Upgrade required for this feature' },
|
|
13
|
+
NOT_ENTITLED: { status: 403, message: 'Feature not available on current tier' },
|
|
14
|
+
OPTION_NOT_ENTITLED: { status: 403, message: 'Option requires higher tier' },
|
|
15
|
+
|
|
16
|
+
// Validation errors
|
|
17
|
+
VALIDATION_ERROR: { status: 400, message: 'Invalid input' },
|
|
18
|
+
NOT_FOUND: { status: 404, message: 'Resource not found' },
|
|
19
|
+
TOOL_NOT_FOUND: { status: 404, message: 'Tool not found' },
|
|
20
|
+
|
|
21
|
+
// Rate limiting
|
|
22
|
+
RATE_LIMITED: { status: 429, message: 'Too many requests' },
|
|
23
|
+
|
|
24
|
+
// Execution errors
|
|
25
|
+
TIMEOUT: { status: 408, message: 'Operation timed out' },
|
|
26
|
+
CANCELLED: { status: 499, message: 'Operation cancelled' },
|
|
27
|
+
INTERNAL_ERROR: { status: 500, message: 'Internal error' },
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Create a standard ErrorEnvelope for tool errors
|
|
32
|
+
* @param {string} code - Error code (AUTH_REQUIRED, TIER_REQUIRED, etc.)
|
|
33
|
+
* @param {string} message - Human-readable error message
|
|
34
|
+
* @param {object} details - Additional error details
|
|
35
|
+
* @returns {object} ErrorEnvelope
|
|
36
|
+
*/
|
|
37
|
+
function createErrorEnvelope(code, message, details = {}) {
|
|
38
|
+
const errorDef = ERROR_CODES[code] || ERROR_CODES.INTERNAL_ERROR;
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
success: false,
|
|
42
|
+
ok: false,
|
|
43
|
+
error: {
|
|
44
|
+
code,
|
|
45
|
+
message: message || errorDef.message,
|
|
46
|
+
status: errorDef.status,
|
|
47
|
+
...details,
|
|
48
|
+
},
|
|
49
|
+
timestamp: new Date().toISOString(),
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Create a success envelope for tool responses
|
|
55
|
+
* @param {*} data - Response data
|
|
56
|
+
* @param {object} metadata - Additional metadata (cacheStats, etc.)
|
|
57
|
+
* @returns {object} SuccessEnvelope
|
|
58
|
+
*/
|
|
59
|
+
function createSuccessEnvelope(data, metadata = {}) {
|
|
60
|
+
return {
|
|
61
|
+
success: true,
|
|
62
|
+
ok: true,
|
|
63
|
+
data,
|
|
64
|
+
...metadata,
|
|
65
|
+
timestamp: new Date().toISOString(),
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Wrap tool execution with consistent error handling, timeout, and cancellation
|
|
71
|
+
* @param {string} toolName - Name of the tool being executed
|
|
72
|
+
* @param {Function} fn - Async function to execute (receives AbortSignal)
|
|
73
|
+
* @param {object} options - Options
|
|
74
|
+
* @param {number} options.timeout - Timeout in milliseconds (default: 30000)
|
|
75
|
+
* @returns {Promise<object>} Success or error envelope
|
|
76
|
+
*/
|
|
77
|
+
async function executeWithEnvelope(toolName, fn, options = {}) {
|
|
78
|
+
const timeout = options.timeout || 30000;
|
|
79
|
+
const controller = new AbortController();
|
|
80
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
81
|
+
|
|
82
|
+
try {
|
|
83
|
+
const result = await Promise.race([
|
|
84
|
+
fn(controller.signal),
|
|
85
|
+
new Promise((_, reject) => {
|
|
86
|
+
controller.signal.addEventListener('abort', () => {
|
|
87
|
+
reject(new Error('TIMEOUT'));
|
|
88
|
+
});
|
|
89
|
+
}),
|
|
90
|
+
]);
|
|
91
|
+
|
|
92
|
+
clearTimeout(timeoutId);
|
|
93
|
+
return createSuccessEnvelope(result);
|
|
94
|
+
} catch (error) {
|
|
95
|
+
clearTimeout(timeoutId);
|
|
96
|
+
|
|
97
|
+
if (error.message === 'TIMEOUT') {
|
|
98
|
+
return createErrorEnvelope(
|
|
99
|
+
'TIMEOUT',
|
|
100
|
+
`${toolName} timed out after ${timeout}ms`,
|
|
101
|
+
{ timeout, toolName }
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (error.name === 'AbortError' || error.message === 'CANCELLED') {
|
|
106
|
+
return createErrorEnvelope(
|
|
107
|
+
'CANCELLED',
|
|
108
|
+
`${toolName} was cancelled`,
|
|
109
|
+
{ toolName }
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// If error already has a code, preserve it
|
|
114
|
+
if (error.code && ERROR_CODES[error.code]) {
|
|
115
|
+
return createErrorEnvelope(
|
|
116
|
+
error.code,
|
|
117
|
+
error.message,
|
|
118
|
+
{ ...error.details, toolName }
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return createErrorEnvelope(
|
|
123
|
+
'INTERNAL_ERROR',
|
|
124
|
+
error.message || 'An unexpected error occurred',
|
|
125
|
+
{
|
|
126
|
+
stack: process.env.NODE_ENV !== 'production' ? error.stack : undefined,
|
|
127
|
+
toolName,
|
|
128
|
+
}
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
module.exports = {
|
|
134
|
+
createErrorEnvelope,
|
|
135
|
+
createSuccessEnvelope,
|
|
136
|
+
executeWithEnvelope,
|
|
137
|
+
ERROR_CODES,
|
|
138
|
+
};
|