Getting Started

Changelog

Release history and what's new in jscpd.

Overview

For the full, up-to-date changelog, see the v5 (Rust) changelog in the jscpd GitHub repository. The v4 (TypeScript) history lives on the master-v4 branch.

v5.2.0 (Latest)

Released 2026-09-08

New Features

  • Type-2 clone detection--ignore-identifiers, --ignore-literals and --ignore-annotations (config keys ignoreIdentifiers, ignoreLiterals, ignoreAnnotations, Action inputs of the same names) find blocks that differ only in names, literal values or annotations. Every clone now carries a kind, exact or renamed; a run without the flags is unchanged apart from the additive "kind": "exact" JSON field. (#998, #1019)
  • Near-miss clone merging with --max-gap-lines N — clones of one file pair separated by at most N unmatched lines in both files are merged into one clone of kind similar with a similarity score, so a copy with a line inserted in the middle is reported once instead of as two halves. Default 0 keeps the old behaviour. (#999, #1020, #1030)
  • Function-level similarity for JavaScript and TypeScript with --similarity RATIO — compares whole functions by syntax-tree structure and reports pairs at or above the ratio as similar clones; names and literals do not take part, so a renamed copy scores 1.0 and a couple of inserted statements about 0.75. The default 1 means exact matches only. The MCP check_duplication tool takes the same argument. See MCP Server. (#999, #1021)
  • Clone kinds in every reporter — console, ai, JSON, XML, HTML, Xcode, SARIF (rules jscpd/renamed-code and jscpd/similar-code) and CodeClimate all show whether a clone is exact, renamed or similar, and how a similar one was found (gap or ast). (#1019, #1021, #1030)
  • Tips are skipped when stdout is not a terminal — pipes, files, CI logs and agent hooks no longer receive the tips and sponsor lines; JSCPD_NO_TIPS joins CI as an environment switch and --no-tips stays. (#1008, #1029)
  • MCP: fully described tool definitions — titles, read-only annotations, parameter examples and descriptions that say when to use each tool; names and schemas are unchanged. See MCP Server. (#1028)

Bug Fixes

  • Config-file ignorePattern entries without * or ? silently did nothing — they were treated as relative paths; they are now applied verbatim, and an invalid regex prints a warning. See Configuration. (#997)
  • JavaScript/TypeScript files with a recoverable parse error could not match clean files — any parser diagnostic sent the file to the fallback tokenizer; tokens now come from the lexer whenever the parser did not fail outright. (#1023, #1024)
  • Markdown inherited the C comment style — a /* or // in prose swallowed the rest of the file; Markdown now has no comment syntax. (#1026)
  • Vue template clones were reported with wrong ranges — the html token stream is now in source order and the wrapper tags are left out of it, so a template clone gets the template's own line range. (#1031)

Other

  • Runnable demos under fixtures/ — every feature and fix above ships a demo directory with a README listing each command and its expected output.
  • Docs: ignore patterns and inline markers are documented in the v5 reference. (#993, #996)

Thank You ❤️

v5.1.2

Released 2026-09-03

New Features

  • Linux ARM64 musl prebuilt binaries — npm installs on Alpine and other musl-based ARM64 Linux systems now get a native binary from the new jscpd-linux-arm64-musl package, bringing the prebuilt platform count to 8. See Platform Binaries. (#988)
  • Docker image ghcr.io/kucherenko/jscpd — a multi-arch (amd64/arm64) distroless image built from the release binaries ships with every release, tagged latest, 5, 5.1 and the exact version, with SLSA provenance and an SBOM. See Using Docker. (#988)
  • cargo-binstall metadata — the crate now points cargo binstall jscpd at the release tarballs. The 5.1.2 metadata still makes binstall fall back to compiling from source because the crate ships two binaries from one archive file; a follow-up release fixes the layout. (#988)

Bug Fixes

  • jscpd --version and --help now say jscpd — the command name was the literal cpd, so jscpd --version printed cpd 5.1.1. The name now follows the invoked executable. (#988)
  • Windows: drive-anchored --pattern values are treated as absolute — patterns like C:\src\**\*.ts were never recognized as absolute because of an impossible character check. (#988)
  • pre-commit hook passed v4-only flags — the hook definition still used --gitignore --exitCode, which the v5 CLI rejects; it now passes --exit-code 1. (#989)
  • Unsupported-platform error is actionable — the npm launchers now name the host (os/arch (libc)), list the supported platforms and point to cargo install jscpd. (#988)

Other

  • Repository split: master is v5-only — the TypeScript v4 engine moved to the master-v4 branch and releases from there under the latest-4 npm dist-tag. See jscpd v4. (#989, #990)
  • Floating v5 tag for the GitHub Actionuses: kucherenko/jscpd@v5 follows the latest 5.x release. (#988)
  • Signed release assets — every release archive carries a Sigstore keyless signature (<asset>.sigstore.json) in addition to SLSA provenance. (#988)
  • crates.io metadata and CI — all crates declare repository, docs, keywords and categories; Windows joined the PR build matrix; a smoke test runs the release binary against the fixtures corpus; nightly cargo audit / cargo deny. (#988)

v5.1.1

Released 2026-08-31

Bug Fixes

  • jscpd on npm installed the 5.0.16 engine instead of 5.1.0 — the jscpd wrapper package published its optionalDependencies pinned to the 5.0.16 platform binaries, so npm i [email protected] resolved a native binary one release behind and jscpd --version reported cpd 5.0.16; everything 5.1.0 fixed was therefore absent for jscpd users, including the Windows --baseline-from-ref fix. The cpd package and the platform packages were unaffected. The release script now updates the wrapper's version and its platform pins independently and fails the release if any pin disagrees with the release version.

Other

  • Declared MSRV corrected to 1.96 — the workspace advertised rust-version = "1.87" on crates.io, a floor the crate could not build on (the oxc parser crates require 1.96). CI now builds at exactly the declared MSRV so it cannot drift again.

v5.1.0

New Features

  • Clone baseline (--baseline, --update-baseline, --fail-on-new-clones) — gate CI on new duplication only: a committed baseline file records fingerprints of accepted clones, and only clones absent from it fail the build, so legacy duplication is tolerated while regressions are caught. See Gate on New Duplication. (#944)
  • Ephemeral baseline from a git ref (--baseline-from-ref) — stateless PR gating without a committed file: scans the base ref in a temporary worktree and reports clones that don't exist there. (#944)
  • OpenMetrics reporter (--reporters openmetrics) — duplication gauges in the OpenMetrics text format, ready for GitLab CI artifacts:reports:metrics. See OpenMetrics Reporter. (#422)
  • CodeClimate / GitLab Code Quality reporter (--reporters codeclimate, alias gitlab)gl-code-quality-report.json for GitLab's artifacts:reports:codequality, so duplicates appear as code quality issues in merge requests. See CodeClimate Reporter. (#958)
  • Windows on ARM support — npm installs pick a native aarch64-pc-windows-msvc binary on Windows ARM64. (#963)
  • Config discovery in .config/ — jscpd also finds its config at .config/jscpd.json per the dot-config convention; a root .jscpd.json still wins. (#979)

Bug Fixes

  • Unknown --format values warn instead of silently scanning 0 files--format cs (or any typo) no longer looks identical to a clean scan in CI. (#964)
  • Nix flake builds again — the Rust toolchain is pinned to an exact patch version, so the manifest hash can no longer drift. (#976)
  • Windows: --baseline-from-ref no longer reports every clone as new — Windows verbatim paths were truncated by the format-suffix stripper, silently breaking snippet reads behind the baseline fingerprints; fingerprints are also line-ending agnostic now.

Thank You ❤️

v5.0.16

New Features

  • MCP server over stdio (--mcp)cpd --mcp /path/to/project serves the Model Context Protocol on stdin/stdout, the transport MCP clients like Claude Desktop, Claude Code, and Cursor spawn and manage themselves — no port, no network policy. The project is scanned once at startup and kept in memory, so snippet checks answer in milliseconds. Tools: check_duplication, get_file_clones, get_statistics, check_current_directory. See MCP Server. (#891)
  • Codebase summary (--summary) — opt-in refactoring-hotspot overview appended to the run output: top files and folders ranked by tokens, lines, size, or a token-based complexity estimate, with each file's duplication share. --summary-top <n> sets the list length, --summary-by tokens|lines|size|complexity picks the metric. Renders in console/console-full, compactly in the ai reporter, and as an additive summary key in the JSON report. (#934)
  • Isolated folder groups (--skip-isolated) — skip duplication between monorepo folders owned by different teams: --skip-isolated "packages/team-a|packages/team-b" drops clones spanning two different folders of the same group, while duplication inside one folder or against shared code is still reported. Config file: "skipIsolated": [["packages/a", "packages/b"]]. (#628)

Security

  • Supply-chain hardening (OpenSSF Scorecard) — GitHub Actions pinned to commit SHAs, least-privilege workflow tokens, SECURITY.md with private vulnerability reporting, protected master branch

Thank You ❤️

  • @hanzhangyu for proposing isolated folder groups for monorepos and contributing the original skipIsolated implementation (#628)

v5.0.15

New Features

  • SARIF: size-based severity — new --sarif-error-tokens <N> flag: clones with at least N tokens are reported at level error while smaller ones stay warning; when duplication exceeds --threshold, all results become error. (#908)
  • SARIF: clone fingerprints — each result carries a partialFingerprints entry (jscpdCloneHash/v1) for cross-run result identity in GitHub code scanning. (#909)
  • SARIF: related-location messages & richer rule metadata — counterpart locations get linked messages, and the jscpd/duplicate-code rule has full metadata for SARIF viewers. (#911, #914)

Bug Fixes

  • Scan-root-relative report paths — report paths are relative to the scanned directory again (as in 4.x); fixes empty snippets and unresolvable paths when scanning from outside the target directory. (#872, #892)
  • Report version stamping — SARIF and HTML report versions now match --version. (#915)
  • Multi-root blame attribution and git root discovery fixes

Thank You ❤️

  • @chrisc-onaorg for the SARIF fingerprints, related-location messages, and rule metadata
  • @darronz for the scan-root-relative paths fix
  • @nvuillam for proposing size-based SARIF severity

v5.0.14

New Features

  • Cross-format clone detection--cross-formats defines groups of related formats sharing one comparison pool, e.g. --cross-formats "javascript,typescript" or the js-ts preset (javascript,jsx,typescript,tsx). When a group mixes TypeScript with JavaScript, TS files are compared with erasable type syntax stripped, so function f(a: number): void matches function f(a). Also configurable as crossFormats in .jscpd.json / package.json. See Configuration → Cross-Format Groups. (#810)

Bug Fixes

  • Prose-only Markdown files are now analyzed.md files without fenced code blocks were silently skipped; duplicated prose is now detected as clones, while embedded code fences keep being detected under their own sub-format pools. (#883)

v5.0.6 – v5.0.13 highlights

  • v5.0.11 — Razor (.razor) tokenizer support
  • v5.0.10 — scan-root-relative paths in all reporters; --skip-local now matches v4 semantics
  • v5.0.9 — GitHub Action for jscpd v5; fixed platform binary resolution for nested installs
  • v5.0.8 — fixed mmap exhaustion on very large repos; fixed --pattern matching with absolute scan roots
  • v5.0.7 — fixed stack overflow on deeply-nested JS/TS; --max-size defaults to 1mb
  • v5.0.6 — v4 config backward compatibility (path, pattern, ignore, ignorePattern); jscpd npm wrapper package; memory-mapped I/O and SIMD line counting
  • v5.0.12 / v5.0.13 — dependency updates and npm packaging fixes

Full details for every release are on GitHub Releases and in the Rust changelog.

v5.0.4

The Rust Rewrite

jscpd v5 is a complete Rust rewrite — a new Rust engine shipped as a self-contained native binary with zero Node.js dependency, bringing parallel detection, near-zero startup cost, and a large performance improvement over the TypeScript engine.

See the Migration Guide for what changed and how to upgrade.

New Features

  • Native binary — no Node.js runtime required. Install via npm install -g jscpd, cargo install jscpd, or direct download.
  • 13 reporters — console, console-full, json, xml, csv, html, markdown, sarif, ai, badge, xcode, threshold, silent.
  • OXC-based JS/TS tokenization — JavaScript/TypeScript/JSX/TSX tokenization now uses the OXC parser instead of PrismJS for better accuracy and speed.
  • Parallel detection--workers N controls thread count. Detection runs in parallel across format groups.
  • CLI alignment — new --absolute, --ignore-case, --formats-exts, --formats-names flags; fixed --threshold; improved --max-size.
  • Side-by-side blame — console-full reporter shows side-by-side blame comparison with --blame.
  • Dual npm packagejscpd@5 and cpd both install the same Rust binary via platform-specific optional dependencies.

Breaking Changes from v4

  • No Node.js APIimport { jscpd } from 'jscpd' is not available in v5. Use the CLI (with JSON output) or the Rust crates instead.
  • No --store leveldb — external store backends are not supported in v5 (detection is fast enough without caching).
  • Reporter name change — v4's full reporter is now console-full in v5.
  • Output filenames — HTML reporter outputs jscpd-report.html at the output directory root.
  • Token counts — may differ by 1-2% due to the Rust tokenizer using OXC for JS/TS.
  • Format count — 223 formats (vs. v4's 224).

v5.0.3

  • Full Rust CLI feature parity with TypeScript jscpd
  • Cross-platform binary distribution via npm platform packages
  • CLI short-form aliases matching TypeScript jscpd conventions
  • Time reporter for execution timing
  • ReportContext struct for extensible reporter signatures

v5.0.2

  • Fixed Vue SFC tokenization (each block dispatched to its own sub-format)
  • Fixed entire-file duplicates silently dropped by RabinKarp store flush logic
  • Fixed ReDoS hang on Lisp/Elisp files
  • Fixed crash on malformed package.json when reading config

v5.0.1

  • Initial Rust workspace with cpd-core, cpd-tokenizer, cpd-finder, cpd-reporter, and jscpd crates
  • Cross-format detection for Vue SFC, Svelte, Astro, and Markdown
  • Shebang detection for extensionless scripts

v5.0.0

  • First stable Rust release — replaces the TypeScript CLI with a native binary
  • Reporter trait signature changed to use ReportContext instead of Statistics directly

v4.2.5

  • JSON reporter token count fix, gitignore parent-directory walk, Commander v15, Vitest 4.1.0

v4.2.3

  • fix(finder): resolve relative ignore patterns against scan dirs (#611)
  • Dependency bumps across all packages

v4.2.2

  • fix(tokenizer): resolve quadratic bash tokenization hang

v4.2.1

  • fix(tokenizer): fix tokenization issue for cross-format detection

v4.2.0

New Features

  • Custom tokenizer backend@jscpd/tokenizer now uses a self-contained reprism-based engine. ~11.5% faster tokenization on real projects (avg 1126 ms → 997 ms on a 548-file, 223-format scan).
  • Cross-format detection — Vue SFC (.vue), Svelte (.svelte), Astro (.astro), and Markdown files are tokenized per-block/per-section, enabling duplicate detection between embedded blocks and standalone source files.
  • 223 supported formats — Apex, CFML/ColdFusion, GDScript, Svelte, Astro, and 70+ additional languages (up from 152). Run jscpd --list to see the full list.
  • Shebang detection — extensionless executable scripts are auto-detected via their #! shebang line.
  • --store-path — specify a custom directory for the LevelDB token cache, eliminating collisions when multiple jscpd processes run concurrently.
  • --skipComments — shorthand for --mode weak. Strips comments before tokenization.
  • --formats-names — map specific filenames (e.g. Makefile, Dockerfile) to a detection format.

Bug Fixes

  • Entire-file duplicates silently dropped (#728) — fixed RabinKarp end-of-file flush bug in @jscpd/core.
  • ReDoS hang on Lisp/Elisp files (#737) — replaced catastrophic backtracking with a linear regex in @jscpd/tokenizer.
  • Process crash on malformed package.json (#739) — now emits a warning and continues.
  • Vue SFC cross-file detection broken — blocks now use resolved sub-format namespace.
  • Vue SFC incorrect column numbers — fixed in @jscpd/tokenizer.
  • 50 dependency security vulnerabilities remediated.

v4.1.1

  • Update hash function, improve performance and keep browser support.

v4.1.0

  • AI reporter (--reporters ai) — compact, token-efficient output for LLMs.
  • --noTips flag — suppress usage-tip messages after detection.
  • Execution timer — timing summary shown after detection completes.
  • Tokenizer speed — lazy Prism grammar loading, O(1) extension lookup, native MD5 hashing.
  • New languages: Apex and CFML (ColdFusion).
  • Test coverage raised to 98%+.

Earlier releases (v1.x – v4.0.x)

See the root CHANGELOG on the master-v4 branch for the full history of jscpd versions 1.0.0 through 4.0.9, covering the tool's origins, the TypeScript rewrite, LevelDB store, pluggable reporters, monorepo extraction, and all the improvements made from 2018 through 2024.