basta is a separate product from jscpd — a different question about the same tree — so it gets its own version, its own prebuilt packages and its own trigger. jscpd goes out on every push to master that leaves a version unpublished; basta goes out only when a `basta-v*` tag is pushed, or by hand through workflow_dispatch. Neither release can drag the other along or hold it up. `basta-release.yml` owns the whole thing: it checks that the crate, the npm manifest and the tag all name the same version and that the working tree is already synced, publishes the crate, builds the eight targets and publishes `basta-<platform>` plus the `basta` wrapper. It ends by asserting that npm's `latest` really points at this release — the package carries 1.x versions from an unrelated tool of the same name, and a dist-tag that quietly stayed on those would hand every `npm i basta` the wrong program. The one place the two cannot be separated is crates.io: `jscpd` depends on `basta`, so cargo refuses to publish jscpd until a matching basta exists. The jscpd crates job therefore still publishes basta when it is missing, and that is now commented as the dependency requirement it is. Both paths skip a version already on the registry, so whichever runs first wins. npm needed the wrapper pattern `jscpd` and `cpd` already use: `rust/basta/` holds the manifest, a platform map naming `basta-<platform>`, and a launcher that falls back to a local `cargo build` when no platform package is installed. `npm-prebuilt-package.mjs` grew a `--product` flag; its output for jscpd is byte-identical to before. `prebuilt-targets.json` now carries a platform label rather than a jscpd-specific blurb, so one table serves both products. `sync-version.mjs` syncs and verifies the basta wrapper against the basta crate version — the same independent version/pin update the jscpd block does, for the same reason it does it. Nothing is published by this commit. Claude-Session: https://claude.ai/code/session_01WztdFKZAwW9b51iJe9zhtV
cpd — Copy/Paste Detector
Fast copy/paste detector for programming source code. Rust rewrite of jscpd, supports 224 language formats.
jscpd v5 is the Rust engine — a self-contained binary. The TypeScript engine (v4) is maintained on the
master-v4branch and published asjscpd@4.
Packages
| Package | Installs | When to use |
|---|---|---|
jscpd |
jscpd |
Default install; the jscpd command |
cpd |
cpd |
Shorter command name only |
basta |
basta |
Dead code, not duplication — a separate binary |
jscpd and cpd install the identical Rust binary. For both jscpd and cpd command names from a single install, use crates.io: cargo install jscpd.
Install
# npm — installs the jscpd command
npm install -g jscpd
# npm — installs only the cpd command
npm install -g cpd
# crates.io — installs both jscpd and cpd binaries
cargo install jscpd
# dead code detection — a separate binary and a separate npm package
npm install -g basta
cargo install basta
# Nix — run without installing
nix run github:kucherenko/jscpd -- /path/to/code
# Nix — install permanently
nix profile install github:kucherenko/jscpd
# Homebrew (macOS/Linux)
brew install jscpd
Prebuilt binaries for 8 platforms (macOS arm64/x64, Linux arm64/x64 with glibc or musl, Windows arm64/x64) — no Node.js runtime required.
Quick Start
# Scan current directory
cpd .
# Scan specific paths
cpd ./src ./lib
# Minimum tokens/lines for a clone
cpd . --min-tokens 30 --min-lines 3
# Git blame with side-by-side author comparison
cpd . --blame --reporters console-full
# Output to JSON + HTML
cpd . --reporters json,html
# Fail CI if duplication exceeds threshold
cpd . --threshold 10
# List all supported formats
cpd --list
Options
| Flag | Short | Default | Description |
|---|---|---|---|
--min-tokens |
-k |
50 | Minimum tokens to consider a duplicate |
--min-lines |
-l |
5 | Minimum lines to consider a duplicate |
--max-lines |
-x |
— | Maximum lines per duplicate block |
--mode |
-m |
mild | Detection mode: mild, weak, strict |
--skip-comments |
— | — | Alias for --mode weak |
--format |
-f |
all | Comma-separated formats to check |
--ignore-pattern |
-i |
— | Glob patterns to ignore |
--reporters |
-r |
console | Comma-separated reporters |
--output |
-o |
report | Output directory for file reporters |
--config |
-c |
— | Path to config file (.jscpd.json) |
--threshold |
-t |
— | Max duplication % before exit 1 |
--blame |
-b |
— | Enrich clones with git blame data |
--skip-local |
— | — | Skip clones within the same directory |
--skip-isolated |
— | — | Skip clones between isolated monorepo folders (e.g. packages/a|packages/b) |
--silent |
-s |
— | Suppress console output |
--list |
— | — | List all supported formats and exit |
For the full options list, see docs/rust.md.
Reporters
| Reporter | Output |
|---|---|
console |
Clone list + statistics table (default) |
console-full |
Source snippets + blame comparison |
json |
report/jscpd-report.json |
html |
report/jscpd-report.html |
sarif |
report/jscpd-report.sarif (GitHub Code Scanning) |
ai |
Token-efficient output for LLM pipelines |
badge |
report/jscpd-badge.svg + report/jscpd-lines-badge.svg |
Plus: xml, csv, markdown, xcode, threshold, silent.
Config File
Create .jscpd.json in your project root:
{
"minTokens": 30,
"minLines": 3,
"format": ["javascript", "typescript", "python"],
"ignorePattern": ["node_modules", "dist", "*.min.js"],
"reporters": ["console", "json"],
"output": "report",
"threshold": 5,
"blame": false
}
Cross-Format Detection
Vue SFC (.vue), Svelte (.svelte), Astro (.astro), and Markdown files are tokenized per-block, enabling duplicate detection across file types.
Programmatic Usage (Rust)
use cpd_finder::orchestrate::{RunConfig, run};
let config = RunConfig {
paths: vec!["./src".into()],
min_tokens: 50,
..Default::default()
};
let result = run(&config).unwrap();
println!("Found {} clones", result.clones.len());
println!("Analyzed {} files", result.statistics.total.sources);
Architecture
cpd (CLI binary)
├── cpd-core — Detection algorithm (Rabin-Karp rolling hash)
├── cpd-tokenizer — Language tokenization (224 formats)
├── cpd-finder — File walking, orchestration, git blame
└── cpd-reporter — Output formatting (15 reporters)
See docs/rust.md for detailed documentation and the v4 migration table, and docs/api.md for the Rust API.
License
MIT