Copy/Paste Detector for Source Code
curl -fsSL https://jscpd.dev/install.sh | bashWindows, npm, pip, cargo, brew, nix and Docker: all install options →
In your editor: VS Code JetBrains IDEs other editors →
Runs inside GitHub Super Linter, MegaLinter and Codacy · 10M+ npm downloads a month
What jscpd does
Four kinds of clones, one scan
jscpd .Type-1: identical tokens. Whitespace, layout and comments do not count. Every run reports these, starting from --min-tokens 50 and --min-lines 5.
jscpd . --ignore-identifiers --ignore-literalsType-2: the same code with different variable names or literal values. --ignore-annotations also drops @Decorator lines. Keywords keep their meaning, so return never matches retry. Reported as renamed.
jscpd . --max-gap-lines 2 --similarity 0.7Type-3: a copy with a few inserted or changed lines (--max-gap-lines N), or a function with the same structure in one of 15 languages (--similarity RATIO, compared by syntax tree). Reported as similar with a score.
jscpd . --semanticType-4: functions that do the same job with different code, even in another language, like a rule that a Rust backend enforces and a Svelte frontend repeats. A code embedding model compares them inside jscpd after a one-time jscpd --semantic-download. Experimental; reported as semantic with a score.
Where it runs
Block the commit before the clone lands.
npx jscpd --threshold 3 .Fail the pull request when duplication crosses the line. SARIF goes to GitHub Code Scanning; GitLab gets Code Quality and metrics reports.
# .github/workflows/jscpd.yml
- uses: kucherenko/jscpd@v5
with:
threshold: 3
Give the agent the same detector over MCP, so it checks for clones before it writes more. Five skills teach it the whole loop: find duplicates, refactor them, remove dead code, check that the numbers improved.
// MCP client config
{ "mcpServers": { "jscpd": {
"command": "jscpd", "args": ["--mcp", "."] } } }
npx skills add kucherenko/jscpdThe same findings as diagnostics while you type, with "Go to the other copy" on every clone.
-- init.lua, Neovim 0.11+
vim.lsp.config('jscpd', { cmd = { 'jscpd', '--lsp' },
root_markers = { '.jscpd.json', '.git' } })
vim.lsp.enable('jscpd')
Who uses jscpd
10M+ npm downloads / month · ~5,000 repositories declare jscpd on GitHub's dependents graph · 15,500+ workflow files reference Super Linter, which runs jscpd by default
Beyond duplicates
--dashboard, --dead-code, --history, --compare, --reporters ai. Boundary rules in the config file keep the code of a module out of the others and fail the build when a copy turns up.