/** * Duplicate Code Detector * * Detects code clones using pure static analysis — no LLM calls: * - Type 1 (exact): identical code after whitespace/comment normalization * - Type 2 (structural): same AST structure with renamed variables * - Type 3 (near): high Jaccard similarity on token n-grams (≥ 0.7) * * Requires a CallGraphResult for precise function boundaries (byte ranges). * Complexity: O(n) for Types 1-2, O(n²) for Type 3 (bounded by MAX_NEAR_FUNCTIONS). */ import type { CallGraphResult } from './call-graph.js'; export interface CloneInstance { file: string; functionName: string; className?: string; startLine: number; endLine: number; } /** 'exact' = identical after normalization, 'structural' = same shape renamed, 'near' = high Jaccard */ export type CloneType = 'exact' | 'structural' | 'near'; export interface CloneGroup { type: CloneType; /** 1.0 for exact/structural; Jaccard similarity for near */ similarity: number; instances: CloneInstance[]; /** Number of lines in the smallest instance of the cloned block */ lineCount: number; } export interface DuplicateDetectionResult { cloneGroups: CloneGroup[]; stats: { /** Functions analyzed (above minimum size threshold) */ totalFunctions: number; /** Functions that appear in at least one clone group */ duplicatedFunctions: number; /** duplicatedFunctions / totalFunctions */ duplicationRatio: number; /** Number of distinct clone groups */ cloneGroupCount: number; }; } /** * Detect duplicate functions across the codebase using the call graph's * function nodes (which carry byte-range boundaries) and the original file * contents. */ export declare function detectDuplicates(files: Array<{ path: string; content: string; }>, callGraph: CallGraphResult): DuplicateDetectionResult; /** Evidence floor (lines) for a clone-query — same threshold as the whole-repo detector. */ export declare const CLONE_MIN_LINES = 5; /** Evidence floor (normalized tokens) for a clone-query — same threshold as the whole-repo detector. */ export declare const CLONE_MIN_TOKENS = 10; /** Default near-clone Jaccard floor — same threshold as the whole-repo detector. */ export declare const CLONE_NEAR_THRESHOLD = 0.7; /** The minimum node shape `findClones` needs — satisfied by both `FunctionNode` and its serialized form. */ export interface CloneQueryNode { filePath: string; name: string; className?: string; startIndex: number; endIndex: number; /** Source language of the node, surfaced on each match so cross-language matches are visible. */ language?: string; } export interface CloneMatch { type: CloneType; /** 1.0 for exact/structural; rounded Jaccard for near. */ similarity: number; file: string; functionName: string; className?: string; startLine: number; endLine: number; /** * The match's source language. Normalization is language-agnostic, so a `near` match CAN be in a * different language than the query (cross-language clones are out of scope — see the tool docs); * surfacing the language makes that disclosed limitation actionable rather than implied by the path. */ language?: string; } export interface CloneQueryOptions { /** Near-clone Jaccard floor for this query (default CLONE_NEAR_THRESHOLD, clamped to [0.1, 1]). */ minSimilarity?: number; /** Cap on returned matches (default: unlimited). */ limit?: number; /** * Exclude the query's own instance (symbol mode), identified by its file + byte range. The byte * range (not the name) is the identity: it is unique per file and collision-proof, so the query is * never wrongly matched against itself and a different function is never wrongly excluded. */ exclude?: { filePath: string; startIndex: number; endIndex: number; }; /** * The query's source language (symbol mode: the node's language; snippet mode: unknown). It * governs the `#` line-comment rule for BOTH the query and every candidate, so the comparison is * one consistent linguistic lens — a `#`-comment Python candidate is never spuriously matched to * a `#`-code TS query. Undefined (snippet mode) falls back to the legacy `#`-as-comment default. */ queryLanguage?: string; } export interface CloneQueryResult { matches: CloneMatch[]; /** Query was below the evidence floor (too few lines/tokens) — no comparison performed. */ belowThreshold: boolean; /** Number of indexed functions actually compared against (above their own evidence floor). */ comparedAgainst: number; /** The near-clone similarity floor used (after clamping). */ similarityFloor: number; } /** * Find the existing functions that are clones of a single query body. * * @param queryBody raw source of the query (a function body, or a snippet) * @param queryLineCount line span of the query (for the evidence floor) * @param files source contents keyed by the same paths the nodes use * @param nodes indexed functions to compare against (Map values or serialized array) */ export declare function findClones(queryBody: string, queryLineCount: number, files: Array<{ path: string; content: string; }>, nodes: Iterable, options?: CloneQueryOptions): CloneQueryResult; //# sourceMappingURL=duplicate-detector.d.ts.map