@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
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Cross-File Analysis Engine
|
|
3
3
|
* Analyzes relationships between files: unused exports, circular dependencies, import consistency
|
|
4
|
-
* Enhanced with monorepo support:
|
|
5
|
-
* - tsconfig.json path alias resolution
|
|
6
|
-
* - package.json exports field support
|
|
7
|
-
* - Workspace package detection
|
|
8
|
-
* - Better barrel export handling
|
|
9
4
|
*/
|
|
10
5
|
|
|
11
6
|
const fs = require("fs");
|
|
@@ -14,162 +9,6 @@ const { getAST } = require("./ast-cache");
|
|
|
14
9
|
const traverse = require("@babel/traverse").default;
|
|
15
10
|
const t = require("@babel/types");
|
|
16
11
|
|
|
17
|
-
/**
|
|
18
|
-
* Load and cache tsconfig.json paths
|
|
19
|
-
*/
|
|
20
|
-
let cachedPathAliases = null;
|
|
21
|
-
let cachedPathsRoot = null;
|
|
22
|
-
|
|
23
|
-
function loadPathAliases(repoRoot) {
|
|
24
|
-
if (cachedPathAliases && cachedPathsRoot === repoRoot) {
|
|
25
|
-
return cachedPathAliases;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
cachedPathsRoot = repoRoot;
|
|
29
|
-
cachedPathAliases = new Map();
|
|
30
|
-
|
|
31
|
-
// Try to load tsconfig.json
|
|
32
|
-
const tsconfigPaths = [
|
|
33
|
-
path.join(repoRoot, "tsconfig.json"),
|
|
34
|
-
path.join(repoRoot, "tsconfig.base.json"),
|
|
35
|
-
path.join(repoRoot, "jsconfig.json"),
|
|
36
|
-
];
|
|
37
|
-
|
|
38
|
-
for (const tsconfigPath of tsconfigPaths) {
|
|
39
|
-
if (fs.existsSync(tsconfigPath)) {
|
|
40
|
-
try {
|
|
41
|
-
// Remove comments from JSON (tsconfig allows them)
|
|
42
|
-
const content = fs.readFileSync(tsconfigPath, "utf8")
|
|
43
|
-
.replace(/\/\*[\s\S]*?\*\//g, "")
|
|
44
|
-
.replace(/\/\/.*$/gm, "");
|
|
45
|
-
const tsconfig = JSON.parse(content);
|
|
46
|
-
const compilerOptions = tsconfig.compilerOptions || {};
|
|
47
|
-
const baseUrl = compilerOptions.baseUrl || ".";
|
|
48
|
-
const paths = compilerOptions.paths || {};
|
|
49
|
-
|
|
50
|
-
for (const [alias, targets] of Object.entries(paths)) {
|
|
51
|
-
// Convert alias pattern to regex
|
|
52
|
-
const aliasPattern = alias.replace(/\*/g, "(.*)");
|
|
53
|
-
const aliasRegex = new RegExp(`^${aliasPattern}$`);
|
|
54
|
-
|
|
55
|
-
cachedPathAliases.set(aliasRegex, {
|
|
56
|
-
alias,
|
|
57
|
-
targets: targets.map(t => path.join(repoRoot, baseUrl, t)),
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
|
-
break; // Use first found config
|
|
61
|
-
} catch (e) {
|
|
62
|
-
// Ignore parse errors
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
return cachedPathAliases;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Resolve path alias to actual path
|
|
72
|
-
*/
|
|
73
|
-
function resolvePathAlias(importPath, repoRoot) {
|
|
74
|
-
const pathAliases = loadPathAliases(repoRoot);
|
|
75
|
-
|
|
76
|
-
for (const [aliasRegex, { alias, targets }] of pathAliases.entries()) {
|
|
77
|
-
const match = importPath.match(aliasRegex);
|
|
78
|
-
if (match) {
|
|
79
|
-
const captured = match[1] || "";
|
|
80
|
-
|
|
81
|
-
for (const target of targets) {
|
|
82
|
-
const resolved = target.replace(/\*/g, captured);
|
|
83
|
-
|
|
84
|
-
// Try common extensions
|
|
85
|
-
const extensions = ["", ".ts", ".tsx", ".js", ".jsx", "/index.ts", "/index.tsx", "/index.js"];
|
|
86
|
-
for (const ext of extensions) {
|
|
87
|
-
const candidate = resolved + ext;
|
|
88
|
-
if (fs.existsSync(candidate)) {
|
|
89
|
-
return candidate;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
return null;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* Detect workspace packages in monorepo
|
|
101
|
-
*/
|
|
102
|
-
let cachedWorkspaces = null;
|
|
103
|
-
|
|
104
|
-
function detectWorkspaces(repoRoot) {
|
|
105
|
-
if (cachedWorkspaces) return cachedWorkspaces;
|
|
106
|
-
|
|
107
|
-
cachedWorkspaces = new Map();
|
|
108
|
-
const packageJsonPath = path.join(repoRoot, "package.json");
|
|
109
|
-
|
|
110
|
-
if (!fs.existsSync(packageJsonPath)) return cachedWorkspaces;
|
|
111
|
-
|
|
112
|
-
try {
|
|
113
|
-
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
|
|
114
|
-
const workspaces = packageJson.workspaces || [];
|
|
115
|
-
|
|
116
|
-
// Handle both array and object format
|
|
117
|
-
const workspacePatterns = Array.isArray(workspaces)
|
|
118
|
-
? workspaces
|
|
119
|
-
: workspaces.packages || [];
|
|
120
|
-
|
|
121
|
-
for (const pattern of workspacePatterns) {
|
|
122
|
-
// Simple glob matching (handles common patterns like "packages/*")
|
|
123
|
-
const baseDir = pattern.replace(/\*.*$/, "");
|
|
124
|
-
const fullBaseDir = path.join(repoRoot, baseDir);
|
|
125
|
-
|
|
126
|
-
if (fs.existsSync(fullBaseDir)) {
|
|
127
|
-
const entries = fs.readdirSync(fullBaseDir, { withFileTypes: true });
|
|
128
|
-
for (const entry of entries) {
|
|
129
|
-
if (entry.isDirectory()) {
|
|
130
|
-
const pkgJsonPath = path.join(fullBaseDir, entry.name, "package.json");
|
|
131
|
-
if (fs.existsSync(pkgJsonPath)) {
|
|
132
|
-
try {
|
|
133
|
-
const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, "utf8"));
|
|
134
|
-
cachedWorkspaces.set(pkgJson.name, {
|
|
135
|
-
name: pkgJson.name,
|
|
136
|
-
path: path.join(baseDir, entry.name),
|
|
137
|
-
exports: pkgJson.exports || {},
|
|
138
|
-
});
|
|
139
|
-
} catch (e) {
|
|
140
|
-
// Ignore parse errors
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
} catch (e) {
|
|
148
|
-
// Ignore errors
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
return cachedWorkspaces;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
/**
|
|
155
|
-
* Check if import is a workspace package
|
|
156
|
-
*/
|
|
157
|
-
function isWorkspaceImport(importPath, repoRoot) {
|
|
158
|
-
const workspaces = detectWorkspaces(repoRoot);
|
|
159
|
-
return workspaces.has(importPath) ||
|
|
160
|
-
Array.from(workspaces.keys()).some(ws => importPath.startsWith(ws + "/"));
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
/**
|
|
164
|
-
* Check if file is a barrel/index file (re-exports)
|
|
165
|
-
*/
|
|
166
|
-
function isBarrelFile(fileRel) {
|
|
167
|
-
return fileRel.endsWith("/index.ts") ||
|
|
168
|
-
fileRel.endsWith("/index.tsx") ||
|
|
169
|
-
fileRel.endsWith("/index.js") ||
|
|
170
|
-
fileRel.endsWith("/index.jsx");
|
|
171
|
-
}
|
|
172
|
-
|
|
173
12
|
/**
|
|
174
13
|
* Build export map for all files
|
|
175
14
|
*/
|
|
@@ -259,43 +98,8 @@ function buildExportMap(files, repoRoot) {
|
|
|
259
98
|
|
|
260
99
|
/**
|
|
261
100
|
* Resolve import path to actual file path
|
|
262
|
-
* Enhanced with path alias and monorepo support
|
|
263
101
|
*/
|
|
264
102
|
function resolveImportPath(importPath, fromFile, repoRoot) {
|
|
265
|
-
// Skip external packages (node_modules)
|
|
266
|
-
if (!importPath.startsWith(".") && !importPath.startsWith("/") && !importPath.startsWith("@/")) {
|
|
267
|
-
// Check if it's a workspace import
|
|
268
|
-
if (isWorkspaceImport(importPath, repoRoot)) {
|
|
269
|
-
// Resolve workspace import
|
|
270
|
-
const workspaces = detectWorkspaces(repoRoot);
|
|
271
|
-
const wsName = Array.from(workspaces.keys()).find(ws =>
|
|
272
|
-
importPath === ws || importPath.startsWith(ws + "/")
|
|
273
|
-
);
|
|
274
|
-
|
|
275
|
-
if (wsName) {
|
|
276
|
-
const ws = workspaces.get(wsName);
|
|
277
|
-
const subPath = importPath.slice(wsName.length + 1) || "index";
|
|
278
|
-
const wsFullPath = path.join(repoRoot, ws.path, "src", subPath);
|
|
279
|
-
|
|
280
|
-
const extensions = ["", ".ts", ".tsx", ".js", ".jsx", "/index.ts", "/index.tsx"];
|
|
281
|
-
for (const ext of extensions) {
|
|
282
|
-
const candidate = wsFullPath + ext;
|
|
283
|
-
if (fs.existsSync(candidate)) {
|
|
284
|
-
return path.relative(repoRoot, candidate).replace(/\\/g, "/");
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
// Try path alias resolution
|
|
291
|
-
const aliasResolved = resolvePathAlias(importPath, repoRoot);
|
|
292
|
-
if (aliasResolved) {
|
|
293
|
-
return path.relative(repoRoot, aliasResolved).replace(/\\/g, "/");
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
return null; // External package
|
|
297
|
-
}
|
|
298
|
-
|
|
299
103
|
const dir = path.dirname(path.join(repoRoot, fromFile));
|
|
300
104
|
let resolved = path.resolve(dir, importPath);
|
|
301
105
|
|
|
@@ -353,41 +157,17 @@ function analyzeCrossFile(files, repoRoot) {
|
|
|
353
157
|
|
|
354
158
|
for (const exp of exports) {
|
|
355
159
|
if (!used.has(exp) && exp !== "default") {
|
|
356
|
-
//
|
|
357
|
-
|
|
358
|
-
// Test/spec files
|
|
359
|
-
fileRel.includes(".test.") ||
|
|
360
|
-
fileRel.includes(".spec.") ||
|
|
361
|
-
fileRel.includes("__tests__") ||
|
|
362
|
-
// Config files
|
|
363
|
-
fileRel.includes("config") ||
|
|
364
|
-
fileRel.includes(".config.") ||
|
|
365
|
-
// Type definitions
|
|
366
|
-
fileRel.endsWith(".d.ts") ||
|
|
367
|
-
// Barrel/index files (re-exports are intentionally not all used)
|
|
368
|
-
isBarrelFile(fileRel) ||
|
|
369
|
-
// Entry points
|
|
370
|
-
fileRel.includes("/pages/") ||
|
|
371
|
-
fileRel.includes("/app/") ||
|
|
372
|
-
fileRel.endsWith("/index.ts") ||
|
|
373
|
-
// API routes (exported handlers are used by framework)
|
|
374
|
-
fileRel.includes("/api/") ||
|
|
375
|
-
// Stories
|
|
376
|
-
fileRel.includes(".stories.") ||
|
|
377
|
-
// Migrations
|
|
378
|
-
fileRel.includes("/migrations/");
|
|
379
|
-
|
|
380
|
-
if (!shouldSkip) {
|
|
160
|
+
// Check if it's a test file or config file (likely to have unused exports)
|
|
161
|
+
if (!fileRel.includes(".test.") && !fileRel.includes(".spec.") && !fileRel.includes("config")) {
|
|
381
162
|
findings.push({
|
|
382
163
|
type: "unused_export",
|
|
383
|
-
severity: "
|
|
164
|
+
severity: "WARN",
|
|
384
165
|
category: "CodeQuality",
|
|
385
166
|
file: fileRel,
|
|
386
|
-
line: 0,
|
|
387
|
-
title: `
|
|
388
|
-
message: `Export "${exp}" from ${fileRel} is
|
|
389
|
-
confidence: "
|
|
390
|
-
fixHint: "Verify if this export is used externally or remove if truly unused",
|
|
167
|
+
line: 0, // Would need to track line numbers
|
|
168
|
+
title: `Unused export: ${exp}`,
|
|
169
|
+
message: `Export "${exp}" from ${fileRel} is never imported`,
|
|
170
|
+
confidence: "med",
|
|
391
171
|
});
|
|
392
172
|
}
|
|
393
173
|
}
|
|
@@ -431,42 +211,20 @@ function analyzeCrossFile(files, repoRoot) {
|
|
|
431
211
|
return null;
|
|
432
212
|
}
|
|
433
213
|
|
|
434
|
-
const reportedCycles = new Set();
|
|
435
|
-
|
|
436
214
|
for (const fileRel of exportMap.keys()) {
|
|
437
215
|
if (!visited.has(fileRel)) {
|
|
438
216
|
const cycle = detectCycle(fileRel);
|
|
439
217
|
if (cycle && cycle.length > 2) {
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
// Check if any files in the cycle are barrel files (common and often harmless)
|
|
452
|
-
const hasBarrel = cycle.some(f => isBarrelFile(f));
|
|
453
|
-
if (hasBarrel) {
|
|
454
|
-
severity = "INFO";
|
|
455
|
-
confidence = "med";
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
findings.push({
|
|
459
|
-
type: "circular_dependency",
|
|
460
|
-
severity,
|
|
461
|
-
category: "CodeQuality",
|
|
462
|
-
file: cycle[0],
|
|
463
|
-
line: 0,
|
|
464
|
-
title: `Circular dependency detected (${cycle.length - 1} files)`,
|
|
465
|
-
message: `Circular dependency: ${cycle.join(" → ")}`,
|
|
466
|
-
confidence,
|
|
467
|
-
fixHint: "Break the cycle by extracting shared code to a separate module, or use dynamic imports",
|
|
468
|
-
});
|
|
469
|
-
}
|
|
218
|
+
findings.push({
|
|
219
|
+
type: "circular_dependency",
|
|
220
|
+
severity: "WARN",
|
|
221
|
+
category: "CodeQuality",
|
|
222
|
+
file: cycle[0],
|
|
223
|
+
line: 0,
|
|
224
|
+
title: `Circular dependency detected`,
|
|
225
|
+
message: `Circular dependency: ${cycle.join(" → ")}`,
|
|
226
|
+
confidence: "high",
|
|
227
|
+
});
|
|
470
228
|
}
|
|
471
229
|
}
|
|
472
230
|
}
|
|
@@ -488,46 +246,23 @@ function analyzeCrossFile(files, repoRoot) {
|
|
|
488
246
|
|
|
489
247
|
for (const [targetFile, styles] of importStyles.entries()) {
|
|
490
248
|
if (styles.size > 1) {
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
file: targetFile,
|
|
502
|
-
line: 0,
|
|
503
|
-
title: `Inconsistent import paths`,
|
|
504
|
-
message: `Module imported with ${uniqueStyles.size} different paths: ${Array.from(uniqueStyles).join(", ")}`,
|
|
505
|
-
confidence: "low",
|
|
506
|
-
fixHint: "Standardize import paths using path aliases (e.g., @/) for consistency",
|
|
507
|
-
});
|
|
508
|
-
}
|
|
249
|
+
findings.push({
|
|
250
|
+
type: "inconsistent_imports",
|
|
251
|
+
severity: "WARN",
|
|
252
|
+
category: "CodeQuality",
|
|
253
|
+
file: targetFile,
|
|
254
|
+
line: 0,
|
|
255
|
+
title: `Inconsistent import paths`,
|
|
256
|
+
message: `Module imported with ${styles.size} different paths: ${Array.from(styles).join(", ")}`,
|
|
257
|
+
confidence: "low",
|
|
258
|
+
});
|
|
509
259
|
}
|
|
510
260
|
}
|
|
511
261
|
|
|
512
262
|
return findings;
|
|
513
263
|
}
|
|
514
264
|
|
|
515
|
-
/**
|
|
516
|
-
* Clear all caches (useful for testing)
|
|
517
|
-
*/
|
|
518
|
-
function clearCaches() {
|
|
519
|
-
cachedPathAliases = null;
|
|
520
|
-
cachedPathsRoot = null;
|
|
521
|
-
cachedWorkspaces = null;
|
|
522
|
-
}
|
|
523
|
-
|
|
524
265
|
module.exports = {
|
|
525
266
|
analyzeCrossFile,
|
|
526
267
|
buildExportMap,
|
|
527
|
-
resolveImportPath,
|
|
528
|
-
loadPathAliases,
|
|
529
|
-
detectWorkspaces,
|
|
530
|
-
isWorkspaceImport,
|
|
531
|
-
isBarrelFile,
|
|
532
|
-
clearCaches,
|
|
533
268
|
};
|
|
@@ -1,95 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Empty Catch Block Detection Engine
|
|
3
3
|
* Uses AST analysis to detect empty or effectively empty catch blocks
|
|
4
|
-
* Enhanced with:
|
|
5
|
-
* - Intentional suppression comment detection
|
|
6
|
-
* - Structured logging detection (winston, pino, bunyan)
|
|
7
|
-
* - Error reporting service detection (Sentry, Bugsnag, Rollbar)
|
|
8
|
-
* - Recovery/cleanup pattern detection
|
|
9
4
|
*/
|
|
10
5
|
|
|
11
6
|
const { getAST, parseCode } = require("./ast-cache");
|
|
12
7
|
const traverse = require("@babel/traverse").default;
|
|
13
8
|
const t = require("@babel/types");
|
|
14
9
|
|
|
15
|
-
/**
|
|
16
|
-
* Comments that indicate intentional error suppression
|
|
17
|
-
*/
|
|
18
|
-
const INTENTIONAL_SUPPRESSION_PATTERNS = [
|
|
19
|
-
/intentionally?\s*(?:ignored?|suppress(?:ed)?|silent|swallow(?:ed)?)/i,
|
|
20
|
-
/error\s*(?:is\s*)?(?:expected|ok|fine|acceptable)/i,
|
|
21
|
-
/safe\s*to\s*ignore/i,
|
|
22
|
-
/this\s*(?:error|exception)\s*(?:can|is)\s*(?:safely\s*)?(?:be\s*)?ignored/i,
|
|
23
|
-
/ignore\s*(?:this\s*)?(?:error|exception)/i,
|
|
24
|
-
/suppress(?:ed)?\s*(?:error|exception)/i,
|
|
25
|
-
/best\s*effort/i,
|
|
26
|
-
/fallback/i,
|
|
27
|
-
/graceful(?:ly)?/i,
|
|
28
|
-
/optional/i,
|
|
29
|
-
/non-critical/i,
|
|
30
|
-
/not\s*(?:a\s*)?(?:critical|fatal)/i,
|
|
31
|
-
];
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Structured logging calls (not console)
|
|
35
|
-
*/
|
|
36
|
-
const STRUCTURED_LOGGER_PATTERNS = [
|
|
37
|
-
// Logger method calls
|
|
38
|
-
/^logger\./i,
|
|
39
|
-
/^log\./i,
|
|
40
|
-
/^winston\./i,
|
|
41
|
-
/^pino\./i,
|
|
42
|
-
/^bunyan\./i,
|
|
43
|
-
/^console\.(error|warn)/i,
|
|
44
|
-
// Error tracking services
|
|
45
|
-
/Sentry\.capture/i,
|
|
46
|
-
/Bugsnag\.notify/i,
|
|
47
|
-
/Rollbar\.error/i,
|
|
48
|
-
/trackError/i,
|
|
49
|
-
/reportError/i,
|
|
50
|
-
/captureException/i,
|
|
51
|
-
/logError/i,
|
|
52
|
-
];
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Check if code contains intentional suppression comment
|
|
56
|
-
*/
|
|
57
|
-
function hasIntentionalSuppressionComment(code, startLine, endLine, lines) {
|
|
58
|
-
// Check comments in and around the catch block
|
|
59
|
-
for (let i = Math.max(0, startLine - 2); i <= Math.min(lines.length - 1, endLine + 1); i++) {
|
|
60
|
-
const line = lines[i];
|
|
61
|
-
// Check for intentional suppression patterns in comments
|
|
62
|
-
if (INTENTIONAL_SUPPRESSION_PATTERNS.some(pattern => pattern.test(line))) {
|
|
63
|
-
return true;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
return false;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Check if expression is a structured logging call
|
|
71
|
-
*/
|
|
72
|
-
function isStructuredLoggingCall(expression, code) {
|
|
73
|
-
if (!t.isCallExpression(expression)) return false;
|
|
74
|
-
|
|
75
|
-
const callee = expression.callee;
|
|
76
|
-
let calleeStr = "";
|
|
77
|
-
|
|
78
|
-
// Build the callee string
|
|
79
|
-
if (t.isMemberExpression(callee)) {
|
|
80
|
-
if (t.isIdentifier(callee.object)) {
|
|
81
|
-
calleeStr = callee.object.name + ".";
|
|
82
|
-
}
|
|
83
|
-
if (t.isIdentifier(callee.property)) {
|
|
84
|
-
calleeStr += callee.property.name;
|
|
85
|
-
}
|
|
86
|
-
} else if (t.isIdentifier(callee)) {
|
|
87
|
-
calleeStr = callee.name;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
return STRUCTURED_LOGGER_PATTERNS.some(pattern => pattern.test(calleeStr));
|
|
91
|
-
}
|
|
92
|
-
|
|
93
10
|
/**
|
|
94
11
|
* Check if a catch block is effectively empty
|
|
95
12
|
*/
|
|
@@ -166,86 +83,59 @@ function analyzeEmptyCatch(code, filePath) {
|
|
|
166
83
|
|
|
167
84
|
if (!body) return;
|
|
168
85
|
|
|
169
|
-
const startLine = catchNode.loc.start.line;
|
|
170
|
-
const endLine = catchNode.loc.end.line;
|
|
171
|
-
|
|
172
|
-
// Check for intentional suppression comments first
|
|
173
|
-
if (hasIntentionalSuppressionComment(code, startLine - 1, endLine - 1, lines)) {
|
|
174
|
-
// Has intentional suppression comment - skip or low severity
|
|
175
|
-
return;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
86
|
// Check if completely empty
|
|
179
87
|
if (isEmptyCatchBlock(body)) {
|
|
88
|
+
const line = catchNode.loc.start.line;
|
|
180
89
|
findings.push({
|
|
181
90
|
type: "empty_catch",
|
|
182
91
|
severity: "WARN",
|
|
183
92
|
category: "EmptyCatch",
|
|
184
93
|
file: filePath,
|
|
185
|
-
line
|
|
94
|
+
line,
|
|
186
95
|
column: catchNode.loc.start.column,
|
|
187
96
|
title: "Empty catch block",
|
|
188
97
|
message: "Catch block contains no error handling code",
|
|
189
|
-
codeSnippet: lines[
|
|
98
|
+
codeSnippet: lines[line - 1]?.trim(),
|
|
190
99
|
confidence: "high",
|
|
191
|
-
fixHint: "Add error handling, logging, or a comment explaining why the error is intentionally ignored",
|
|
192
100
|
});
|
|
193
101
|
return;
|
|
194
102
|
}
|
|
195
103
|
|
|
196
|
-
// Check if only has comments - but allow console.error/warn
|
|
104
|
+
// Check if only has comments - but allow console.error/warn and return/throw
|
|
197
105
|
const statements = body.body || [];
|
|
198
106
|
const hasRealHandling = statements.some(stmt => {
|
|
199
|
-
// Allow return statements
|
|
107
|
+
// Allow return statements
|
|
200
108
|
if (t.isReturnStatement(stmt)) return true;
|
|
201
|
-
|
|
202
|
-
// Allow throw statements (re-throwing or wrapping)
|
|
109
|
+
// Allow throw statements
|
|
203
110
|
if (t.isThrowStatement(stmt)) return true;
|
|
204
|
-
|
|
205
|
-
// Allow structured logging and error reporting
|
|
111
|
+
// Allow console.error/warn (logging is acceptable error handling)
|
|
206
112
|
if (t.isExpressionStatement(stmt) && t.isCallExpression(stmt.expression)) {
|
|
207
|
-
|
|
208
|
-
|
|
113
|
+
const callee = stmt.expression.callee;
|
|
114
|
+
if (t.isMemberExpression(callee) &&
|
|
115
|
+
t.isIdentifier(callee.object, { name: "console" }) &&
|
|
116
|
+
t.isIdentifier(callee.property)) {
|
|
117
|
+
if (["error", "warn"].includes(callee.property.name)) {
|
|
118
|
+
return true; // This is acceptable error handling
|
|
119
|
+
}
|
|
209
120
|
}
|
|
210
121
|
}
|
|
211
|
-
|
|
212
|
-
// Allow await expressions (might be async error handling)
|
|
213
|
-
if (t.isExpressionStatement(stmt) && t.isAwaitExpression(stmt.expression)) {
|
|
214
|
-
return true;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
// Allow variable assignments (might be setting error state)
|
|
218
|
-
if (t.isExpressionStatement(stmt) && t.isAssignmentExpression(stmt.expression)) {
|
|
219
|
-
return true;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
// Allow variable declarations (const error = ...)
|
|
223
|
-
if (t.isVariableDeclaration(stmt)) {
|
|
224
|
-
return true;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
// Allow if statements (conditional error handling)
|
|
228
|
-
if (t.isIfStatement(stmt)) {
|
|
229
|
-
return true;
|
|
230
|
-
}
|
|
231
|
-
|
|
232
122
|
return false;
|
|
233
123
|
});
|
|
234
124
|
|
|
235
125
|
// Only flag if it truly has no handling
|
|
236
126
|
if (!hasRealHandling && onlyHasComments(body, code)) {
|
|
127
|
+
const line = catchNode.loc.start.line;
|
|
237
128
|
findings.push({
|
|
238
129
|
type: "comment_only_catch",
|
|
239
130
|
severity: "WARN",
|
|
240
131
|
category: "EmptyCatch",
|
|
241
132
|
file: filePath,
|
|
242
|
-
line
|
|
133
|
+
line,
|
|
243
134
|
column: catchNode.loc.start.column,
|
|
244
135
|
title: "Catch block with only comments",
|
|
245
136
|
message: "Catch block contains only comments, no actual error handling",
|
|
246
|
-
codeSnippet: lines[
|
|
137
|
+
codeSnippet: lines[line - 1]?.trim(),
|
|
247
138
|
confidence: "med",
|
|
248
|
-
fixHint: "Add proper error handling or use a recognized suppression comment pattern",
|
|
249
139
|
});
|
|
250
140
|
}
|
|
251
141
|
},
|