@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,354 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Duplicate Code Detection Engine
|
|
3
|
-
* Detects:
|
|
4
|
-
* - Exact duplicate code blocks
|
|
5
|
-
* - Similar code patterns (near-duplicates)
|
|
6
|
-
* - Copy-paste code between files
|
|
7
|
-
* - Repeated utility functions
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
const { getAST } = require("./ast-cache");
|
|
11
|
-
const traverse = require("@babel/traverse").default;
|
|
12
|
-
const t = require("@babel/types");
|
|
13
|
-
const crypto = require("crypto");
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Minimum lines for a duplicate to be reported
|
|
17
|
-
*/
|
|
18
|
-
const MIN_DUPLICATE_LINES = 5;
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Minimum tokens for a duplicate to be considered significant
|
|
22
|
-
*/
|
|
23
|
-
const MIN_DUPLICATE_TOKENS = 20;
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Similarity threshold for near-duplicates (0-1)
|
|
27
|
-
*/
|
|
28
|
-
const SIMILARITY_THRESHOLD = 0.8;
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Normalize code by removing variable names and literals
|
|
32
|
-
*/
|
|
33
|
-
function normalizeCode(code) {
|
|
34
|
-
// Remove comments
|
|
35
|
-
code = code.replace(/\/\/.*$/gm, "");
|
|
36
|
-
code = code.replace(/\/\*[\s\S]*?\*\//g, "");
|
|
37
|
-
|
|
38
|
-
// Normalize whitespace
|
|
39
|
-
code = code.replace(/\s+/g, " ").trim();
|
|
40
|
-
|
|
41
|
-
// Remove string contents (keep quotes)
|
|
42
|
-
code = code.replace(/"[^"]*"/g, '""');
|
|
43
|
-
code = code.replace(/'[^']*'/g, "''");
|
|
44
|
-
code = code.replace(/`[^`]*`/g, "``");
|
|
45
|
-
|
|
46
|
-
// Normalize numbers
|
|
47
|
-
code = code.replace(/\b\d+\.?\d*\b/g, "0");
|
|
48
|
-
|
|
49
|
-
return code;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Tokenize code for comparison
|
|
54
|
-
*/
|
|
55
|
-
function tokenize(code) {
|
|
56
|
-
const normalized = normalizeCode(code);
|
|
57
|
-
|
|
58
|
-
// Split into tokens
|
|
59
|
-
const tokens = normalized.split(/(\s+|[{}()\[\];,.])/g)
|
|
60
|
-
.filter(t => t.trim().length > 0);
|
|
61
|
-
|
|
62
|
-
return tokens;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Calculate hash of normalized code block
|
|
67
|
-
*/
|
|
68
|
-
function hashCodeBlock(code) {
|
|
69
|
-
const normalized = normalizeCode(code);
|
|
70
|
-
return crypto.createHash("md5").update(normalized).digest("hex");
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Calculate similarity between two token arrays using Jaccard similarity
|
|
75
|
-
*/
|
|
76
|
-
function calculateSimilarity(tokens1, tokens2) {
|
|
77
|
-
if (tokens1.length === 0 || tokens2.length === 0) return 0;
|
|
78
|
-
|
|
79
|
-
const set1 = new Set(tokens1);
|
|
80
|
-
const set2 = new Set(tokens2);
|
|
81
|
-
|
|
82
|
-
let intersection = 0;
|
|
83
|
-
for (const token of set1) {
|
|
84
|
-
if (set2.has(token)) intersection++;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
const union = set1.size + set2.size - intersection;
|
|
88
|
-
return intersection / union;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Extract significant code blocks from AST
|
|
93
|
-
*/
|
|
94
|
-
function extractCodeBlocks(code, filePath) {
|
|
95
|
-
const ast = getAST(code, filePath);
|
|
96
|
-
if (!ast) return [];
|
|
97
|
-
|
|
98
|
-
const blocks = [];
|
|
99
|
-
const lines = code.split("\n");
|
|
100
|
-
|
|
101
|
-
traverse(ast, {
|
|
102
|
-
// Function declarations and expressions
|
|
103
|
-
FunctionDeclaration(path) {
|
|
104
|
-
extractBlock(path, blocks, code, lines, "function");
|
|
105
|
-
},
|
|
106
|
-
FunctionExpression(path) {
|
|
107
|
-
// Only capture if it's a standalone function (not inline callback)
|
|
108
|
-
if (path.parentPath.isVariableDeclarator()) {
|
|
109
|
-
extractBlock(path, blocks, code, lines, "function");
|
|
110
|
-
}
|
|
111
|
-
},
|
|
112
|
-
ArrowFunctionExpression(path) {
|
|
113
|
-
// Only capture if it's a standalone function
|
|
114
|
-
if (path.parentPath.isVariableDeclarator()) {
|
|
115
|
-
const body = path.node.body;
|
|
116
|
-
// Only capture functions with block body (not one-liners)
|
|
117
|
-
if (t.isBlockStatement(body)) {
|
|
118
|
-
extractBlock(path, blocks, code, lines, "arrow");
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
},
|
|
122
|
-
|
|
123
|
-
// Class methods
|
|
124
|
-
ClassMethod(path) {
|
|
125
|
-
extractBlock(path, blocks, code, lines, "method");
|
|
126
|
-
},
|
|
127
|
-
|
|
128
|
-
// If statements with significant bodies
|
|
129
|
-
IfStatement(path) {
|
|
130
|
-
const bodyLines = getBlockLineCount(path.node);
|
|
131
|
-
if (bodyLines >= MIN_DUPLICATE_LINES) {
|
|
132
|
-
extractBlock(path, blocks, code, lines, "if-block");
|
|
133
|
-
}
|
|
134
|
-
},
|
|
135
|
-
|
|
136
|
-
// Switch statements
|
|
137
|
-
SwitchStatement(path) {
|
|
138
|
-
const bodyLines = getBlockLineCount(path.node);
|
|
139
|
-
if (bodyLines >= MIN_DUPLICATE_LINES) {
|
|
140
|
-
extractBlock(path, blocks, code, lines, "switch");
|
|
141
|
-
}
|
|
142
|
-
},
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
return blocks;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* Get line count of a block
|
|
150
|
-
*/
|
|
151
|
-
function getBlockLineCount(node) {
|
|
152
|
-
if (!node.loc) return 0;
|
|
153
|
-
return node.loc.end.line - node.loc.start.line + 1;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
/**
|
|
157
|
-
* Extract a code block with metadata
|
|
158
|
-
*/
|
|
159
|
-
function extractBlock(path, blocks, code, lines, type) {
|
|
160
|
-
const node = path.node;
|
|
161
|
-
if (!node.loc) return;
|
|
162
|
-
|
|
163
|
-
const startLine = node.loc.start.line;
|
|
164
|
-
const endLine = node.loc.end.line;
|
|
165
|
-
const lineCount = endLine - startLine + 1;
|
|
166
|
-
|
|
167
|
-
// Skip small blocks
|
|
168
|
-
if (lineCount < MIN_DUPLICATE_LINES) return;
|
|
169
|
-
|
|
170
|
-
const blockCode = code.substring(node.start, node.end);
|
|
171
|
-
const tokens = tokenize(blockCode);
|
|
172
|
-
|
|
173
|
-
// Skip blocks with too few tokens
|
|
174
|
-
if (tokens.length < MIN_DUPLICATE_TOKENS) return;
|
|
175
|
-
|
|
176
|
-
// Get function/method name if available
|
|
177
|
-
let name = "<anonymous>";
|
|
178
|
-
if (node.id?.name) {
|
|
179
|
-
name = node.id.name;
|
|
180
|
-
} else if (node.key?.name) {
|
|
181
|
-
name = node.key.name;
|
|
182
|
-
} else if (path.parentPath?.isVariableDeclarator()) {
|
|
183
|
-
const declId = path.parentPath.node.id;
|
|
184
|
-
if (t.isIdentifier(declId)) {
|
|
185
|
-
name = declId.name;
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
blocks.push({
|
|
190
|
-
type,
|
|
191
|
-
name,
|
|
192
|
-
startLine,
|
|
193
|
-
endLine,
|
|
194
|
-
lineCount,
|
|
195
|
-
code: blockCode,
|
|
196
|
-
tokens,
|
|
197
|
-
hash: hashCodeBlock(blockCode),
|
|
198
|
-
snippet: lines[startLine - 1]?.trim() || "",
|
|
199
|
-
});
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
/**
|
|
203
|
-
* Find duplicates within a single file
|
|
204
|
-
*/
|
|
205
|
-
function findDuplicatesInFile(code, filePath) {
|
|
206
|
-
const findings = [];
|
|
207
|
-
const blocks = extractCodeBlocks(code, filePath);
|
|
208
|
-
|
|
209
|
-
// Group by hash for exact duplicates
|
|
210
|
-
const hashGroups = new Map();
|
|
211
|
-
for (const block of blocks) {
|
|
212
|
-
if (!hashGroups.has(block.hash)) {
|
|
213
|
-
hashGroups.set(block.hash, []);
|
|
214
|
-
}
|
|
215
|
-
hashGroups.get(block.hash).push(block);
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
// Report exact duplicates
|
|
219
|
-
for (const [hash, group] of hashGroups) {
|
|
220
|
-
if (group.length > 1) {
|
|
221
|
-
const first = group[0];
|
|
222
|
-
const locations = group.map(b => `line ${b.startLine}`).join(", ");
|
|
223
|
-
|
|
224
|
-
findings.push({
|
|
225
|
-
type: "exact_duplicate",
|
|
226
|
-
severity: "WARN",
|
|
227
|
-
category: "CodeQuality",
|
|
228
|
-
file: filePath,
|
|
229
|
-
line: first.startLine,
|
|
230
|
-
column: 0,
|
|
231
|
-
title: `Duplicate code block (${first.lineCount} lines, ${group.length} occurrences)`,
|
|
232
|
-
message: `Identical code found at: ${locations}. Consider extracting to a shared function.`,
|
|
233
|
-
codeSnippet: first.snippet,
|
|
234
|
-
confidence: "high",
|
|
235
|
-
fixHint: "Extract duplicate code into a reusable function",
|
|
236
|
-
});
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
// Find near-duplicates (similar but not identical)
|
|
241
|
-
const reported = new Set();
|
|
242
|
-
for (let i = 0; i < blocks.length; i++) {
|
|
243
|
-
for (let j = i + 1; j < blocks.length; j++) {
|
|
244
|
-
const block1 = blocks[i];
|
|
245
|
-
const block2 = blocks[j];
|
|
246
|
-
|
|
247
|
-
// Skip if already matched as exact duplicate
|
|
248
|
-
if (block1.hash === block2.hash) continue;
|
|
249
|
-
|
|
250
|
-
// Skip if already reported
|
|
251
|
-
const pairKey = `${block1.startLine}-${block2.startLine}`;
|
|
252
|
-
if (reported.has(pairKey)) continue;
|
|
253
|
-
|
|
254
|
-
const similarity = calculateSimilarity(block1.tokens, block2.tokens);
|
|
255
|
-
|
|
256
|
-
if (similarity >= SIMILARITY_THRESHOLD) {
|
|
257
|
-
reported.add(pairKey);
|
|
258
|
-
|
|
259
|
-
findings.push({
|
|
260
|
-
type: "similar_code",
|
|
261
|
-
severity: "INFO",
|
|
262
|
-
category: "CodeQuality",
|
|
263
|
-
file: filePath,
|
|
264
|
-
line: block1.startLine,
|
|
265
|
-
column: 0,
|
|
266
|
-
title: `Similar code blocks (${Math.round(similarity * 100)}% similar)`,
|
|
267
|
-
message: `'${block1.name}' (line ${block1.startLine}) and '${block2.name}' (line ${block2.startLine}) have similar structure.`,
|
|
268
|
-
codeSnippet: block1.snippet,
|
|
269
|
-
confidence: "med",
|
|
270
|
-
fixHint: "Consider refactoring to share common logic",
|
|
271
|
-
});
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
return findings;
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
/**
|
|
280
|
-
* Find duplicates across multiple files
|
|
281
|
-
*/
|
|
282
|
-
function findDuplicatesAcrossFiles(files, repoRoot) {
|
|
283
|
-
const fs = require("fs");
|
|
284
|
-
const path = require("path");
|
|
285
|
-
const findings = [];
|
|
286
|
-
const allBlocks = new Map(); // hash -> [{ file, block }]
|
|
287
|
-
|
|
288
|
-
// Collect blocks from all files
|
|
289
|
-
for (const fileAbs of files) {
|
|
290
|
-
try {
|
|
291
|
-
const code = fs.readFileSync(fileAbs, "utf8");
|
|
292
|
-
const fileRel = path.relative(repoRoot, fileAbs).replace(/\\/g, "/");
|
|
293
|
-
const blocks = extractCodeBlocks(code, fileRel);
|
|
294
|
-
|
|
295
|
-
for (const block of blocks) {
|
|
296
|
-
if (!allBlocks.has(block.hash)) {
|
|
297
|
-
allBlocks.set(block.hash, []);
|
|
298
|
-
}
|
|
299
|
-
allBlocks.get(block.hash).push({ file: fileRel, block });
|
|
300
|
-
}
|
|
301
|
-
} catch (e) {
|
|
302
|
-
// Skip files that can't be read
|
|
303
|
-
continue;
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
// Find cross-file duplicates
|
|
308
|
-
for (const [hash, occurrences] of allBlocks) {
|
|
309
|
-
// Get unique files
|
|
310
|
-
const uniqueFiles = new Set(occurrences.map(o => o.file));
|
|
311
|
-
|
|
312
|
-
if (uniqueFiles.size > 1) {
|
|
313
|
-
const first = occurrences[0];
|
|
314
|
-
const fileList = Array.from(uniqueFiles).slice(0, 5).join(", ");
|
|
315
|
-
const moreFiles = uniqueFiles.size > 5 ? ` and ${uniqueFiles.size - 5} more` : "";
|
|
316
|
-
|
|
317
|
-
findings.push({
|
|
318
|
-
type: "cross_file_duplicate",
|
|
319
|
-
severity: "WARN",
|
|
320
|
-
category: "CodeQuality",
|
|
321
|
-
file: first.file,
|
|
322
|
-
line: first.block.startLine,
|
|
323
|
-
column: 0,
|
|
324
|
-
title: `Duplicate code across ${uniqueFiles.size} files`,
|
|
325
|
-
message: `Same code block (${first.block.lineCount} lines) found in: ${fileList}${moreFiles}`,
|
|
326
|
-
codeSnippet: first.block.snippet,
|
|
327
|
-
confidence: "high",
|
|
328
|
-
fixHint: "Extract to a shared utility module",
|
|
329
|
-
});
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
return findings;
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
/**
|
|
337
|
-
* Analyze duplicate code in a single file
|
|
338
|
-
*/
|
|
339
|
-
function analyzeDuplicateCode(code, filePath) {
|
|
340
|
-
return findDuplicatesInFile(code, filePath);
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
module.exports = {
|
|
344
|
-
analyzeDuplicateCode,
|
|
345
|
-
findDuplicatesInFile,
|
|
346
|
-
findDuplicatesAcrossFiles,
|
|
347
|
-
extractCodeBlocks,
|
|
348
|
-
calculateSimilarity,
|
|
349
|
-
normalizeCode,
|
|
350
|
-
tokenize,
|
|
351
|
-
hashCodeBlock,
|
|
352
|
-
MIN_DUPLICATE_LINES,
|
|
353
|
-
SIMILARITY_THRESHOLD,
|
|
354
|
-
};
|