# What changed in jscpd

> 238 changes in jscpd 4 to 5 from its official release notes, upgrade guides and advisories: 14 breaking, 1 deprecations, 6 security, 120 new APIs and options. Each one cited. Sources checked 2026-10-18. HTML: https://unstale.sh/npm/jscpd/

To see which of these apply to a repository, run `npx @unstale/cli init` in it.

## jscpd 5

### Breaking changes

- The minimum Rust version is now 1.97. (5.4.1) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.4.1)
- Compares function structure across 15 languages and supported code blocks using Jaccard similarity. It finds every pair meeting the ratio; scores differ from 5.4.0, and a ratio of 1 now reports identical structures. (5.4.1) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.4.1)
- Similarity pairs now use their own SARIF rule instead of `jscpd/similar-code`, changing alert identity for GitHub code scanning and Code Climate check names. (5.4.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.4.0)
- Ignore-pattern matching in files with CRLF endings now removes the intended tokens, which can change clone token counts. (5.3.3) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.3.3)
- Dead-code configuration precedence is flag over the deadCode section over older top-level keys; misspelled keys in the section are reported by name. (5.3.1) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.3.1)
- Unknown formats, nonexistent scan paths, and reporter file-write failures now print an error and exit 1 instead of passing with an empty report. (5.2.1) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.2.1)
- The declared minimum Rust version is corrected to 1.96, the floor required to build the crate. (5.1.1) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.1.1)
- The CLI binary and platform npm packages are now named jscpd / jscpd-* instead of cpd / cpd-* to avoid antivirus false positives. (5.0.12) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.0.12)
- When absolute is false or unset, all reporters now emit paths relative to the canonicalized scan root and strip leading ./ or .\ components. (5.0.10) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.0.10)
- Now filters clones whose fragments are under the same scan root, matching jscpd v4 TypeScript semantics rather than limiting the filter to the same parent directory. (5.0.10) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.0.10)
- File-level globs and code-level regex patterns are no longer conflated. (5.0.6) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.0.6)

### Security

- An API key echoed by an embeddings API in an error is now redacted. (5.4.1) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.4.1)
- Dashboard and health reports, including markdown and HTML output, escape untrusted file paths, custom format names and external metric IDs before rendering. (5.3.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.3.0)
- Each release archive and `checksums.txt` now has a Sigstore keyless signature that can be verified with this command. (5.1.2) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.1.2)
- The SARIF schema URI now uses HTTPS, avoiding "untrusted URI" errors in SARIF-consuming tools. (5.0.12) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.0.12)

### Deprecations

- The ineffective flag is hidden from help and warns when passed, but remains accepted; use `--threshold`, `--min-lines`, or `--min-tokens` for the stated purposes. (5.3.3) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.3.3)

### New APIs and options

- Selects the file used to save or read the baseline for `--changed`. (5.4.1) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.4.1)
- Reports clones involving files listed by `git status`, including staged, unstaged and untracked files, while still scanning every file. A baseline marks clones absent from HEAD as `[NEW]`. (5.4.1) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.4.1)
- Scans only changed files and matches them only against one another. (5.4.1) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.4.1)
- The MCP server adds `compare_folders(left, right)`, which returns the JSON report of `--compare` for two folders. (5.4.1) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.4.1)
- The `-r edn` reporter writes `jscpd-report.edn`, including similarity candidates with scores, languages, locations and node counts, plus clones found by other passes. (5.4.1) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.4.1)
- MCP tools accept clone kinds to report, including exact, renamed, similar, semantic, or type1 through type4. (5.4.1) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.4.1)
- Sets the smallest normalized function tree considered by similarity matching; defaults to 20. (5.4.1) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.4.1)
- The JSON report now gives the node count of both functions in a similarity result. (5.4.1) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.4.1)
- Sets the base name for JSON, XML, CSV, HTML, Markdown, SARIF and EDN report files to avoid collisions in a shared reports folder. (5.4.1) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.4.1)
- An installable agent skill guides test-first codebase ports, binding tests to code by coverage and selecting functions to port from `--compare`. (5.4.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.4.0)
- An installable agent skill explains how `--compare` pairs functions and how to check a comparison. (5.4.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.4.0)
- Compares two codebases by pairing functions with the semantic model, reporting counterparts, unmatched functions, similarity levels, and functions ready to port. (5.4.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.4.0)
- Starts a language server that reports diagnostics for edited buffers, supports configurable analyses, and provides clone actions and project-wide custom requests. (5.4.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.4.0)
- Selects which language-server analyses to enable; clones are enabled by default, and editor settings can override this option. (5.4.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.4.0)
- Lowers smaller clone diagnostics from warnings to information in the language server. (5.4.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.4.0)
- Provides the API key for semantic embeddings requests; jscpd reads it only from this environment variable. (5.3.3) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.3.3)
- The `semantic` value filters results to semantic clones. (5.3.3) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.3.3)
- Replaces the instruction prefix jscpd adds before functions for models that expect one. (5.3.3) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.3.3)
- Downloads a semantic embedding model before scanning; the default model runs locally on the CPU. (5.3.3) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.3.3)
- Selects a semantic embedding model by its listed name, Hugging Face id, or Ollama name. (5.3.3) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.3.3)
- Lists the calibrated semantic models, including their thresholds, licenses, and execution locations. (5.3.3) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.3.3)
- Re-embeds all functions and replaces the semantic vector cache file at once. (5.3.3) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.3.3)
- Sets the minimum cosine similarity for a semantic clone pair within one language; thresholds vary by model. (5.3.3) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.3.3)
- Restricts semantic clone pairs to the same language or to different languages. (5.3.3) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.3.3)
- Sets the minimum cosine similarity for a semantic clone pair across languages; thresholds vary by model. (5.3.3) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.3.3)
- Sends functions to an OpenAI-compatible embeddings API; the API key is read only from `JSCPD_SEMANTIC_API_KEY`. (5.3.3) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.3.3)
- Experimental semantic clone detection embeds supported functions and reports sufficiently similar pairs as semantic clones. (5.3.3) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.3.3)
- The `semantic` config key enables experimental semantic clone detection. (5.3.3) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.3.3)
- The dead-code config section accepts a Rust diagnostics file, allowing Rust compiler diagnostics to be used for dead-code reporting. (5.3.2) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.3.2)
- jscpd accepts compiler JSON diagnostics via a file or pipe for dead-code, dashboard, and health runs, enabling Rust dead-code reporting without running cargo. (5.3.2) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.3.2)
- basta discovers framework definitions from basta.frameworks.yaml, basta.frameworks.yml or basta.frameworks.json in the working directory. (5.3.1) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.3.1)
- The standalone basta binary accepts -c, --config <file> to select its configuration file. (5.3.1) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.3.1)
- The .jscpd.json deadCode setting can be an object with enabled, categories, confidence and line thresholds, entry and ignore settings, reporting controls, and framework settings. The object enables the mode only when enabled is true; the existing boolean switch still works. (5.3.1) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.3.1)
- Treats a named framework as present, useful when a scan starts below its package.json. (5.3.1) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.3.1)
- Loads custom framework definitions from a YAML or JSON file; definitions can add frameworks or replace built-ins with the same name. (5.3.1) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.3.1)
- Framework definitions can declare names looked up in code; declarations with those names are treated as used, and the names-and-files form scopes them to selected files. (5.3.1) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.3.1)
- Prints the list of framework definitions. (5.3.1) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.3.1)
- Turns framework detection off. (5.3.1) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.3.1)
- The dashboard JSON adds project.largestFiles, containing the ranked list of largest code files. (5.3.1) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.3.1)
- Dead-code threshold can be set inside deadCode; if omitted there, a dead-code run still falls back to the top-level threshold. (5.3.1) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.3.1)
- The dead-code analysis engine is also available as a standalone command. (5.3.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.3.0)
- Reports complexity without running a clone scan, providing the complexity portion of `--summary`. (5.3.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.3.0)
- Adds a one-run report combining health information, project size, duplication by format, complex files and dead-code findings by category. (5.3.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.3.0)
- Finds unused files, exports and imports across JavaScript, TypeScript, Vue, Svelte, Astro and Python using the project's import graph and real entry points; findings include confidence scores. (5.3.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.3.0)
- Adds a 0–100 health score with an A–E grade based on duplication, dead code and complexity, adjusted for project size and excluding unmeasurable dimensions. (5.3.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.3.0)
- Lets you plug custom metrics, such as coverage or security scan results, into the health score. (5.3.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.3.0)
- Adds HTML output for both dashboard and health reports. (5.3.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.3.0)
- Filters clones by whether they were found as exact, renamed or similar matches; similar matches distinguish gap merges from structural matches, and typos are caught. (5.3.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.3.0)
- Adds markdown output for both dashboard and health reports. (5.3.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.3.0)
- Makes a run fail when no files were analyzed, so a mistyped path or over-broad ignore cannot look like a clean run. (5.2.1) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.2.1)
- Adds a GitHub Action input that makes a run fail when no files were analyzed. (5.2.1) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.2.1)
- Makes a run fail when no files were analyzed, so a mistyped path or over-broad ignore cannot look like a clean run. (5.2.1) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.2.1)
- Scans every commit in a Git range and prints duplication trends, per-commit changes, and how far --threshold could be tightened without failing the build. (5.2.1) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.2.1)
- Adds a flag to narrow the commit range scanned by --history. (5.2.1) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.2.1)
- Adds a flag to narrow the commit range scanned by --history. (5.2.1) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.2.1)
- Adds a flag to narrow the commit range scanned by --history. (5.2.1) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.2.1)
- The JSON reporter includes history points under a new history key. (5.2.1) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.2.1)
- The GitHub Action accepts a history input for duplication trend scans. (5.2.1) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.2.1)
- The MCP check_duplication tool accepts the similarity argument for function-level similarity detection. (5.2.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.2.0)
- Reporters distinguish renamed and similar clones; JSON and XML include clone metadata, statistics expose renamedClones and similarClones, and SARIF and Code Climate use dedicated rules. (5.2.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.2.0)
- Drops supported annotation runs before hashing so code blocks that differ only in annotations can be detected as renamed clones. (5.2.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.2.0)
- Normalizes identifiers before hashing so code blocks that differ only in names can be detected as renamed clones. (5.2.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.2.0)
- Normalizes literal values before hashing so code blocks that differ only in literals can be detected as renamed clones. (5.2.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.2.0)
- Drops supported annotation runs before hashing so code blocks that differ only in annotations can be detected as renamed clones. (5.2.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.2.0)
- Normalizes identifiers before hashing so code blocks that differ only in names can be detected as renamed clones. (5.2.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.2.0)
- Normalizes literal values before hashing so code blocks that differ only in literals can be detected as renamed clones. (5.2.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.2.0)
- Every clone carries a kind of exact or renamed; runs without the new normalization flags remain unchanged apart from the additive exact JSON field. (5.2.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.2.0)
- Merges nearby clone fragments from one file pair into a similar clone when at most N unmatched lines separate them in both files; defaults to off. (5.2.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.2.0)
- Merges nearby clone fragments from one file pair into a similar clone when at most N unmatched lines separate them in both files; defaults to off. (5.2.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.2.0)
- Compares JavaScript and TypeScript functions, methods, and arrow functions by syntax-tree 4-grams and reports pairs meeting the ratio as similar clones. (5.2.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.2.0)
- Compares JavaScript and TypeScript functions, methods, and arrow functions by syntax-tree 4-grams and reports pairs meeting the ratio as similar clones. (5.2.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.2.0)
- npm installs on musl-based ARM64 Linux systems now select a native prebuilt binary from this platform package. (5.1.2) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.1.2)
- Compare current clones with fingerprints scanned from a base Git ref using a temporary detached worktree, without a committed baseline file. (5.1.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.1.0)
- Use a committed baseline of accepted clone fingerprints so CI can distinguish new duplication from legacy duplication. (5.1.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.1.0)
- Configure the committed clone baseline file with the `baseline` config key. (5.1.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.1.0)
- Configure the Git ref used as an ephemeral clone baseline. (5.1.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.1.0)
- Add `.config/.jscpd.json` to automatic config discovery. (5.1.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.1.0)
- Add `.config/jscpd.json` to automatic config discovery, after the root `.jscpd.json` and before the `package.json` `jscpd` key. (5.1.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.1.0)
- Exit 1 when new clones exceed the specified count, defaulting to zero, independently of `--threshold`. (5.1.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.1.0)
- Configure the maximum number of new clones allowed before the run fails. (5.1.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.1.0)
- npm installs on Windows ARM64 now select a native binary from this platform package. (5.1.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.1.0)
- Report new clones through console markers and counts, JSON fields and statistics, SARIF errors, and OpenMetrics gauges. (5.1.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.1.0)
- Select a CodeClimate/GitLab Code Quality reporter that writes `gl-code-quality-report.json` with clone issues and deterministic fingerprints. (5.1.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.1.0)
- Add `gitlab` as an alias for the CodeClimate/GitLab Code Quality reporter. (5.1.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.1.0)
- Select a reporter that writes OpenMetrics text exposition to `jscpd-metrics.txt`, including duplication and detection-duration gauges. (5.1.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.1.0)
- Rewrite the baseline from the current run, creating it if missing, and print added and removed fingerprint counts. (5.1.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.1.0)
- The MCP server provides a tool that returns the clone list. (5.0.16) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.0.16)
- The MCP server provides snippet checks that accept format names or file extensions. (5.0.16) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.0.16)
- The MCP server provides clone lookup for one file, a capability described as new over the HTTP server. (5.0.16) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.0.16)
- The MCP server provides a statistics tool. (5.0.16) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.0.16)
- Starts an MCP server over stdio, scanning the project once and keeping detection-ready token hashes in memory for snippet checks. Standard detection options apply to the scan and snippet checks. (5.0.16) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.0.16)
- Accepts comma-separated isolation groups of pipe-separated folders and drops clones whose fragments fall under different folders of the same group. It also applies to MCP project scans. (5.0.16) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.0.16)
- Accepts a nested-array shape to declare isolated folder groups in the config file. (5.0.16) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.0.16)
- Opts into a refactoring-hotspot overview appended to run output, ranking top files and folders by a selected metric and showing each file's duplication share. (5.0.16) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.0.16)
- Selects whether the codebase summary ranks by tokens, lines, size, or a token-based cyclomatic-complexity estimate. (5.0.16) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.0.16)
- Sets the list length for the codebase summary. (5.0.16) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.0.16)
- The config-file setting enables the refactoring-hotspot summary. (5.0.16) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.0.16)
- When the summary flag is enabled, the JSON report adds a `summary` key; it is absent when the flag is off, preserving the schema for existing consumers. (5.0.16) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.0.16)
- Selects the codebase summary ranking metric in the config file. (5.0.16) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.0.16)
- Sets the codebase summary list length in the config file. (5.0.16) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.0.16)
- SARIF results include an order-insensitive clone hash for cross-run identity, alongside token_count and clone_hash in the properties bag. (5.0.15) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.0.15)
- Reports clones with at least N tokens as SARIF errors and smaller clones as warnings; when overall duplication exceeds --threshold, all SARIF results are errors. (5.0.15) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.0.15)
- Sets the token count at which clones are reported as SARIF errors; when overall duplication exceeds --threshold, all SARIF results are errors. (5.0.15) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.0.15)
- Detect clones across related formats using comparison groups, including the js-ts preset; TypeScript is compared with erasable type syntax stripped when grouped with JavaScript. (5.0.14) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.0.14)
- Configure cross-format clone detection in `.jscpd.json` or `package.json` with a string, array of strings, or array of arrays. (5.0.14) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.0.14)
- Adds a tokenizer for Razor files that parses HTML content and Razor keyword blocks. (5.0.11) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.0.11)
- The path config field reads scan directories and resolves relative paths against the config file's directory. (5.0.6) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.0.6)
- Adds the --absolute CLI flag as part of alignment with jscpd v4. (5.0.4) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.0.4)
- The console reporter displays a clone list. (5.0.4) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.0.4)
- The console-full reporter adds side-by-side blame comparison. (5.0.4) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.0.4)
- Adds the --formats-exts CLI flag as part of alignment with jscpd v4. (5.0.4) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.0.4)
- Adds the --formats-names CLI flag as part of alignment with jscpd v4. (5.0.4) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.0.4)
- Adds the --ignore-case CLI flag as part of alignment with jscpd v4. (5.0.4) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.0.4)

### Performance

- The module index can quickly determine whether a path could name a module under any extension or as a directory index, avoiding expensive resolver misses for catch-all aliases. (5.3.1) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.3.1)
- basta checks whether an alias pattern matches an import before comparing paths, substantially reducing dead-code scan time on projects with many aliases without changing findings. (5.3.1) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.3.1)
- Memory-mapped file I/O, SIMD line counting, a parallel detection pipeline, and avoiding tokenizer source-string clones improve performance and reduce allocations. (5.0.6) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.0.6)

### Behavior and default changes

- The MCP tool now finds every copy of a snippet instead of matching one of two. (5.4.1) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.4.1)
- The Project section ranks the largest code files by lines, with tokens and size; json, markdown and html reporters include the same list. (5.3.1) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.3.1)
- basta detects frameworks from built-in definitions and keeps files they load alive, checking each scanned-file directory independently. (5.3.1) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.3.1)
- basta reads the deadCode section from the same configuration file as jscpd and searches .jscpd.json, .config/jscpd.json or package.json by default. (5.3.1) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.3.1)
- The top-level minLines remains a clone-reporting setting and is not applied to dead-code runs; dead-code minLines is configured inside deadCode. (5.3.1) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.3.1)
- Nitro framework detection follows srcDir and treats tasks/ as framework-loaded. (5.3.1) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.3.1)
- The `--summary --summary-by complexity` estimate more closely tracks cyclomatic complexity, accounting for short-circuit operators and language-specific branches and measuring complexity per function. (5.3.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.3.0)
- Tips and sponsor lines print only on an interactive terminal, preventing them from appearing in piped output, files, CI logs, or agent hooks. (5.2.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.2.0)
- The counterpart location gets a message and the primary message links to it, enabling GitHub code scanning to display the relationship. (5.0.15) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.0.15)
- A CLI option now accepts an optional integer value, with distinct exit codes; two options are now independent. (5.0.6) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.0.6)
- Several previously unsupported v4 configuration fields are now read and applied, matching jscpd v4 behavior. (5.0.6) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.0.6)
- Improves the --max-size CLI flag. (5.0.4) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.0.4)

### Tooling

- The release publishes platform wheels installable with pip or uvx, providing the Rust engine without Python code or a Node.js runtime. (5.2.1) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.2.1)
- The four MCP tools now include titles, read-only annotations, parameter descriptions with examples and defaults, and descriptions of use and return values; server instructions describe the workflow. (5.2.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.2.0)
- The crate now provides cargo-binstall metadata so this command downloads a prebuilt binary instead of compiling the oxc parser stack from source. (5.1.2) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.1.2)
- A multi-arch amd64/arm64 distroless Docker image built from release binaries is published with every release. (5.1.2) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.1.2)
- The SARIF rule now includes a display name, full description, default configuration, and quality tags for improved presentation in SARIF viewers and Azure DevOps. (5.0.15) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.0.15)
- A GitHub Action is available to scan repositories for copy/paste in CI. (5.0.9) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.0.9)
- An npm package publishes the same Rust binary with v5.x versioning. (5.0.6) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.0.6)

### Fixes and other changes

- Comparison fixes include counting test helpers among call namesakes, recognizing .NET test projects wherever `Tests` appears in a dotted name, and aligning console and HTML percentages; declarations without bodies no longer count as functions. (5.4.1) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.4.1)
- Imports referenced in TSDoc links and JavaScript JSDoc types now count as used; documentation-only references do not make a function live. (5.4.1) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.4.1)
- Git subprocesses drop variables exported to hooks, refs or history ranges beginning with `-` are refused, and worktree cleanup preserves the user's other worktrees. (5.4.1) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.4.1)
- Ignored ranges now align with offsets in Markdown fences, component scripts and Razor blocks, so the intended tokens are excluded. (5.4.1) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.4.1)
- The MCP server no longer ignores `--semantic`. (5.4.1) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.4.1)
- C, C++ and C# scans no longer treat control-flow keywords around preprocessor directives as phantom functions or count every matching keyword call. (5.4.1) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.4.1)
- Svelte store reads using `$name` and template-literal placeholders in component markup now count as uses, avoiding false unused-symbol findings. (5.3.3) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.3.3)
- Files under a scanned path that is nested inside another scanned path are read once instead of twice, preventing self-clones and duplicate semantic embeddings. (5.3.3) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.3.3)
- JSON report positions for files with CRLF endings now use true byte offsets. (5.3.3) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.3.3)
- Embedded sources now count only the lines occupied by their code blocks, and clones count the block lines they cover, preventing surrounding prose or templates from inflating line totals. (5.3.2) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.3.2)
- Duplicated-line counts now include both the start and end lines of each clone, so totals rise by about one line per clone and percentages rise with them. (5.3.2) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.3.2)
- Alembic is detected as a framework, preventing its loaded files from being reported unused. (5.3.1) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.3.1)
- Django is detected as a framework, so migrations, management commands, admin.py, apps.py, template tags and modules named by dotted settings paths are not incorrectly reported unused. (5.3.1) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.3.1)
- The markup block of a component file tokenized as html is excluded from the duplication share, matching css and scss blocks. (5.3.1) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.3.1)
- Markup, stylesheet and template files now have complexity 0 and are excluded from the health score's code count; component script blocks still count. (5.3.1) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.3.1)
- The JSON report can include path-appears-in-string as a reason when a file's extensionless path appears in a string literal; this reduces its confidence by 40 points. (5.3.1) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.3.1)
- Static require calls using a template literal with no interpolation are recognized. (5.3.1) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.3.1)
- Scrapy is detected as a framework, preventing its loaded files from being reported unused. (5.3.1) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.3.1)
- Arrow functions in component attributes no longer cause the tag parser to mistake the arrow's > for the end of the tag and fail parsing the script. (5.3.1) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.3.1)
- A leading slash in a Vite alias target is resolved from the project root rather than the filesystem root, preventing imported components from being reported unused. (5.3.1) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.3.1)
- A wxt.config.* marks entrypoints and auto-import directories as entries, honors srcDir, entrypointsDir and imports: false, and declares conventional aliases. (5.3.1) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.3.1)
- Plain text, log and CSV files now handle comments differently; the notes do not specify further details. (5.3.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.3.0)
- With `--fail-on-empty`, complexity mode writes its reports before failing, matching other modes. (5.3.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.3.0)
- Dashboard and health reports omit the dead-code section rather than failing when `--format` excludes every analyzable language. (5.3.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.3.0)
- Excluding markup duplication from scoring now properly excludes it on both sides of the calculation. (5.3.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.3.0)
- A mistyped field no longer invalidates the whole configuration; only the bad field is dropped and the rest still applies. (5.3.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.3.0)
- Values above 100 are clamped with a warning everywhere the option is read, not only in standalone dead-code mode. (5.3.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.3.0)
- Windows report paths now use forward slashes consistently with other platforms. (5.3.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.3.0)
- Clone growth now checks whether the clone's own anchor continues and starts a new clone otherwise, preventing N-way copies from losing pairs or producing mismatched ends. (5.2.1) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.2.1)
- Prints the --history block, matching the console reporter. (5.2.1) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.2.1)
- Files retain the path they were found at, and each real file is scanned once, avoiding renamed paths, duplicate sources, and self-clones through symlinks. (5.2.1) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.2.1)
- Replaces XML 1.0-invalid characters with U+FFFD, splits ]]> across CDATA sections, and escapes paths once so generated reports can be parsed. (5.2.1) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.2.1)
- Config-file entries are now applied verbatim instead of being treated as relative paths, so patterns without * or ? work; invalid regexes produce a warning. (5.2.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.2.0)
- Files with recoverable parser diagnostics now use lexer tokens unless parsing fails outright, allowing them to match clean files. (5.2.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.2.0)
- Markdown no longer inherits C comment syntax, preventing prose containing /* or // from swallowing the rest of a file during clone detection. (5.2.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.2.0)
- Vue template tokens are kept in source order and wrapper tags are excluded, correcting template clone line ranges and preventing script and style bodies from counting as duplicated HTML. (5.2.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.2.0)
- The pre-commit hook now passes the v5 CLI flag `--exit-code 1` instead of rejected v4-only flags, so repository hooks no longer fail on every run. (5.1.2) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.1.2)
- The version output and help usage line now use the invoked executable name, jscpd or cpd, instead of always saying cpd. (5.1.2) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.1.2)
- On Windows, drive-anchored pattern values such as `C:\src\**\*.ts` are now treated as absolute instead of also receiving a relative `**/` variant. (5.1.2) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.1.2)
- When no prebuilt binary matches, npm launchers name the host, list supported platform keys, and point to `cargo install jscpd`. (5.1.2) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.1.2)
- The floating GitHub Action tag `v5` follows the latest 5.x release and is moved on every stable release. (5.1.2) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.1.2)
- The npm wrapper now pins its platform binaries to the release version, so installing jscpd no longer resolves a native binary one release behind. (5.1.1) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.1.1)
- On Windows, ephemeral baselines now match clone fingerprints instead of reporting every clone as new. (5.1.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.1.0)
- Fingerprint hashing strips CR characters, so committed baselines survive CRLF/LF differences between platforms. (5.1.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.1.0)
- Unsupported format values now produce a stderr warning naming the value and pointing to `--list` instead of silently scanning zero files. (5.1.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.1.0)
- Pinning the Rust toolchain to an exact patch version fixes flake builds that failed when the mutable Rust manifest changed. (5.1.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.1.0)
- Walking up from a relative scan path no longer stops before reaching the repository root. (5.0.15) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.0.15)
- Git blame data is keyed by resolved path when multiple scan roots contain the same relative path, preventing the second root from inheriting the first root's authors. (5.0.15) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.0.15)
- The SARIF tool driver and HTML report versions now match cpd --version instead of using a hardcoded SARIF version. (5.0.15) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.0.15)
- Report paths are relative to the scanned directory while reporters can still resolve and read source files, fixing empty snippets and unresolvable paths, including in multi-root scans. (5.0.15) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.0.15)
- Markdown prose is now tokenized, so prose-only files are analyzed and duplicated prose can be detected as clones; embedded code fences remain detected under their own sub-format pools. (5.0.14) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.0.14)
- Lisp, Clojure, Scheme, and Racket semicolon comments are recognized as comments, allowing --mode weak and --skip-comments to drop them. (5.0.12) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.0.12)
- When installed as a nested dependency, the runner locates the platform-specific binary relative to the installed package instead of assuming a top-level install. (5.0.9) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.0.9)
- The walker opens and drops each file mapping within the processing closure, capping concurrent mappings to the thread-pool size and preventing mmap exhaustion crashes. (5.0.8) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.0.8)
- Patterns match against both the relative path and the full absolute path when the scan root is absolute; bare patterns gain a prefix to match at any depth. (5.0.8) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.0.8)
- Scanning directories with deeply nested JS/TS files no longer risks stack overflow; parsing uses a local rayon pool with 64 MiB stacks. (5.0.7) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.0.7)
- Files exceeding the size limit are skipped during directory walking, preventing OXC from processing megabyte-scale generated files. (5.0.7) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.0.7)
- Detection and statistics are aligned with jscpd for consistent output across Rust and TypeScript versions. (5.0.4) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.0.4)
- The HTML reporter now outputs jscpd-report.html at the output_dir root. (5.0.4) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.0.4)
- Fixes the --threshold CLI flag. (5.0.4) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v5.0.4)

## jscpd 4

### Breaking changes

- The server now binds to 127.0.0.1 by default. (4.3.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v4.3.0)
- Custom mappings that assign .vue to markup no longer take effect because .vue is handled by the dedicated vue format processor. (4.2.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v4.2.0)
- Vue SFC blocks are tokenized and reported under their resolved sub-format names instead of markup; tooling relying on markup reports must be updated. (4.2.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v4.2.0)

### Security

- Adds a Host allowlist option for DNS-rebinding protection on both /mcp and the REST API. (4.3.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v4.3.0)
- Adds an Origin allowlist option for DNS-rebinding protection on both /mcp and the REST API. (4.3.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v4.3.0)

### New APIs and options

- Adds a config key to override automatic ANSI color detection; explicit flag or config settings take precedence over FORCE_COLOR, NO_COLOR, and TTY detection. (4.3.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v4.3.0)
- Adds a flag to override automatic ANSI color detection. (4.3.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v4.3.0)
- Adds a flag to disable ANSI colors, overriding automatic detection. (4.3.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v4.3.0)
- Maps specific filenames, such as Makefile and Dockerfile, to a detection format. (4.2.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v4.2.0)
- Extensionless executable scripts are detected from their #! shebang line and tokenized in the corresponding language. (4.2.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v4.2.0)
- Adds a shorthand flag for --mode weak, which strips comments before detection. (4.2.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v4.2.0)
- Configures a custom directory for the LevelDB cache, avoiding collisions when multiple jscpd processes run in parallel on the same machine. (4.2.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v4.2.0)

### Performance

- The new self-contained reprism-based grammar engine replaces prismjs and improves tokenization speed by about 11.5% on real projects. (4.2.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v4.2.0)

### Behavior and default changes

- ANSI colors are disabled automatically when stdout is not a TTY, including for the statistics table. (4.3.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v4.3.0)
- jscpd-server migrates to the official MCP SDK v2 and stateless 2026-07-28 revision, with a legacy fallback for 2025-era clients. (4.3.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v4.3.0)
- Vue, Svelte, Astro, and Markdown files are tokenized per block or section, allowing their code to match files in the corresponding language. (4.2.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v4.2.0)

### Tooling

- Support increased from 152 to 223 formats, including Apex, CFML/ColdFusion, GDScript, Svelte, and Astro. (4.2.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v4.2.0)

### Fixes and other changes

- Skips the progress announcer for ai and consoleFull reporters that print every clone themselves, preventing duplicate output. (4.3.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v4.3.0)
- Server output now respects NO_COLOR and FORCE_COLOR and detects TTY status like the CLI. (4.3.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v4.3.0)
- Fixes off-by-one source line counts in statistics and reports. (4.3.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v4.3.0)
- Ignore files in parent directories up to the repository root are combined with scan-directory ignore files; Git info and global excludes are also read. (4.2.5) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v4.2.5)
- Duplicate token counts in JSON output are now computed from token positions. (4.2.5) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v4.2.5)
- The Commander upgrade enables modern Node.js compatibility. (4.2.5) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v4.2.5)
- Complete copies of files are detected instead of being silently dropped when RabinKarp flushes a pending clone. (4.2.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v4.2.0)
- Replaces a catastrophically backtracking Lisp string regex with a linear pattern, preventing hangs on unterminated strings. (4.2.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v4.2.0)
- Invalid JSON now produces a warning and an empty config instead of an unhandled SyntaxError that crashes the process. (4.2.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v4.2.0)
- Tokens on the first line of a Vue SFC block now use file-absolute rather than block-relative column numbers. (4.2.0) — [release notes](https://github.com/kucherenko/jscpd/releases/tag/v4.2.0)
