[{"data":1,"prerenderedAt":916},["ShallowReactive",2],{"navigation_docs":3,"blog-posts":244},[4,29,99,119,189],{"title":5,"icon":6,"path":7,"stem":8,"children":9,"page":-1},"Start",false,"\u002Fstart","1.start",[10,14,19,24],{"title":11,"path":7,"stem":12,"icon":13},"What jscpd finds","1.start\u002Findex","i-lucide-house",{"title":15,"path":16,"stem":17,"icon":18},"Installation","\u002Fstart\u002Finstallation","1.start\u002F2.installation","i-lucide-download",{"title":20,"path":21,"stem":22,"icon":23},"Quickstart","\u002Fstart\u002Fquickstart","1.start\u002F3.quickstart","i-lucide-rocket",{"title":25,"path":26,"stem":27,"icon":28},"Configuration basics","\u002Fstart\u002Fconfiguration","1.start\u002F4.configuration","i-lucide-settings-2",{"title":30,"icon":6,"path":31,"stem":32,"children":33,"page":6},"Guides","\u002Fguides","2.guides",[34,39,44,49,54,59,64,69,74,79,84,89,94],{"title":35,"path":36,"stem":37,"icon":38},"Use jscpd in CI","\u002Fguides\u002Fci","2.guides\u002F01.ci","i-lucide-github",{"title":40,"path":41,"stem":42,"icon":43},"Pre-commit hooks","\u002Fguides\u002Fpre-commit","2.guides\u002F02.pre-commit","i-lucide-git-commit",{"title":45,"path":46,"stem":47,"icon":48},"Fail only on new duplication","\u002Fguides\u002Fbaseline","2.guides\u002F03.baseline","i-lucide-git-compare",{"title":50,"path":51,"stem":52,"icon":53},"Monorepos","\u002Fguides\u002Fmonorepos","2.guides\u002F04.monorepos","i-lucide-folder-tree",{"title":55,"path":56,"stem":57,"icon":58},"Cross-format detection","\u002Fguides\u002Fcross-format","2.guides\u002F05.cross-format","i-lucide-layers",{"title":60,"path":61,"stem":62,"icon":63},"Agents","\u002Fguides\u002Fagents","2.guides\u002F06.agents","i-lucide-cpu",{"title":65,"path":66,"stem":67,"icon":68},"Editors","\u002Fguides\u002Feditors","2.guides\u002F07.editors","i-lucide-code",{"title":70,"path":71,"stem":72,"icon":73},"Duplication over git history","\u002Fguides\u002Fhistory","2.guides\u002F08.history","i-lucide-chart-no-axes-combined",{"title":75,"path":76,"stem":77,"icon":78},"Dashboard and health score","\u002Fguides\u002Fdashboard","2.guides\u002F09.dashboard","i-lucide-layout-dashboard",{"title":80,"path":81,"stem":82,"icon":83},"Dead code","\u002Fguides\u002Fdead-code","2.guides\u002F10.dead-code","i-lucide-ghost",{"title":85,"path":86,"stem":87,"icon":88},"Comparing two codebases","\u002Fguides\u002Fcompare","2.guides\u002F11.compare","i-lucide-git-compare-arrows",{"title":90,"path":91,"stem":92,"icon":93},"Semantic clones","\u002Fguides\u002Fsemantic-clones","2.guides\u002F12.semantic-clones","i-lucide-brain-circuit",{"title":95,"path":96,"stem":97,"icon":98},"Similar functions","\u002Fguides\u002Fsimilarity","2.guides\u002F13.similarity","i-lucide-list-tree",{"title":100,"icon":6,"path":101,"stem":102,"children":103,"page":6},"Concepts","\u002Fconcepts","3.concepts",[104,109,114],{"title":105,"path":106,"stem":107,"icon":108},"Types of code clones","\u002Fconcepts\u002Fclone-types","3.concepts\u002F1.clone-types","i-lucide-copy",{"title":110,"path":111,"stem":112,"icon":113},"How detection works","\u002Fconcepts\u002Fhow-detection-works","3.concepts\u002F2.how-detection-works","i-lucide-scan-search",{"title":115,"path":116,"stem":117,"icon":118},"The health score","\u002Fconcepts\u002Fhealth-score","3.concepts\u002F3.health-score","i-lucide-heart-pulse",{"title":120,"icon":6,"path":121,"stem":122,"children":123,"page":6},"Reference","\u002Freference","4.reference",[124,129,134,139,176,181,185],{"title":125,"path":126,"stem":127,"icon":128},"CLI options","\u002Freference\u002Fcli","4.reference\u002F1.cli","i-lucide-terminal",{"title":130,"path":131,"stem":132,"icon":133},"The config file","\u002Freference\u002Fconfig-file","4.reference\u002F2.config-file","i-lucide-settings",{"title":135,"path":136,"stem":137,"icon":138},"Exit codes","\u002Freference\u002Fexit-codes","4.reference\u002F3.exit-codes","i-lucide-log-out",{"title":140,"icon":6,"path":141,"stem":142,"children":143,"page":-1},"Reporters","\u002Freference\u002Freporters","4.reference\u002F4.reporters\u002Findex",[144,146,151,156,161,166,171],{"title":140,"path":141,"stem":142,"icon":145},"i-lucide-file-chart-column",{"title":147,"path":148,"stem":149,"icon":150},"HTML reporter","\u002Freference\u002Freporters\u002Fhtml","4.reference\u002F4.reporters\u002F2.html","i-lucide-file-code",{"title":152,"path":153,"stem":154,"icon":155},"JSON reporter","\u002Freference\u002Freporters\u002Fjson","4.reference\u002F4.reporters\u002F3.json","i-lucide-braces",{"title":157,"path":158,"stem":159,"icon":160},"Badge reporter","\u002Freference\u002Freporters\u002Fbadge","4.reference\u002F4.reporters\u002F4.badge","i-lucide-award",{"title":162,"path":163,"stem":164,"icon":165},"SARIF reporter","\u002Freference\u002Freporters\u002Fsarif","4.reference\u002F4.reporters\u002F5.sarif","i-lucide-shield-check",{"title":167,"path":168,"stem":169,"icon":170},"CodeClimate reporter","\u002Freference\u002Freporters\u002Fcodeclimate","4.reference\u002F4.reporters\u002F6.codeclimate","i-lucide-flag-triangle-right",{"title":172,"path":173,"stem":174,"icon":175},"OpenMetrics reporter","\u002Freference\u002Freporters\u002Fopenmetrics","4.reference\u002F4.reporters\u002F7.openmetrics","i-lucide-activity",{"title":177,"path":178,"stem":179,"icon":180},"Supported formats","\u002Freference\u002Fsupported-formats","4.reference\u002F5.supported-formats","i-lucide-languages",{"title":182,"path":183,"stem":184,"icon":38},"GitHub Action","\u002Freference\u002Fgithub-action","4.reference\u002F6.github-action",{"title":186,"path":187,"stem":188,"icon":68},"Rust crates","\u002Freference\u002Frust-crates","4.reference\u002F7.rust-crates",{"title":190,"icon":6,"path":191,"stem":192,"children":193,"page":6},"Project","\u002Fproject","5.project",[194,199,204,209,234,239],{"title":195,"path":196,"stem":197,"icon":198},"Changelog","\u002Fproject\u002Fchangelog","5.project\u002F1.changelog","i-lucide-scroll-text",{"title":200,"path":201,"stem":202,"icon":203},"Migrating from v4 to v5","\u002Fproject\u002Fmigration","5.project\u002F2.migration","i-lucide-arrow-right-left",{"title":205,"path":206,"stem":207,"icon":208},"jscpd v4 (TypeScript)","\u002Fproject\u002Fv4","5.project\u002F3.v4","i-simple-icons-typescript",{"title":210,"icon":6,"path":211,"stem":212,"children":213,"page":-1},"Benchmarks","\u002Fproject\u002Fbenchmarks","5.project\u002F4.benchmarks\u002Findex",[214,216,221,225,230],{"title":210,"path":211,"stem":212,"icon":215},"i-lucide-gauge",{"title":217,"path":218,"stem":219,"icon":220},"Detection Speed","\u002Fproject\u002Fbenchmarks\u002Fdetection-speed","5.project\u002F4.benchmarks\u002F2.detection-speed","i-lucide-zap",{"title":222,"path":223,"stem":224,"icon":58},"Multi-Format Detection","\u002Fproject\u002Fbenchmarks\u002Fcross-format","5.project\u002F4.benchmarks\u002F3.cross-format",{"title":226,"path":227,"stem":228,"icon":229},"AI Token Efficiency","\u002Fproject\u002Fbenchmarks\u002Fai-token-efficiency","5.project\u002F4.benchmarks\u002F4.ai-token-efficiency","i-lucide-sparkles",{"title":231,"path":232,"stem":233,"icon":93},"Embedding Models","\u002Fproject\u002Fbenchmarks\u002Fembedding-models","5.project\u002F4.benchmarks\u002F5.embedding-models",{"title":235,"path":236,"stem":237,"icon":238},"Research","\u002Fproject\u002Fresearch","5.project\u002F5.research","i-lucide-graduation-cap",{"title":240,"path":241,"stem":242,"icon":243},"Articles","\u002Fproject\u002Farticles","5.project\u002F6.articles","i-lucide-newspaper",[245],{"id":246,"title":247,"authors":248,"body":250,"date":905,"description":906,"extension":907,"meta":908,"navigation":6,"path":909,"seo":910,"stem":912,"tags":913,"__hash__":915},"blog\u002Fblog\u002Fjscpd-5-4.md","What's new in jscpd 5.4 and 5.4.1",[249],"kucherenko",{"type":251,"value":252,"toc":897},"minimark",[253,274,279,285,288,291,355,370,403,406,456,467,474,547,553,557,574,585,626,646,650,664,667,675,689,693,699,705,730,734,824,828,870,893],[254,255,256,257,261,262,265,266,273],"p",{},"Two releases in nine days, so one post for both. 5.4.0 added a language server and a way to compare two codebases, 5.4.1 rewrote ",[258,259,260],"code",{},"--similarity"," from scratch and added ",[258,263,264],{},"--changed",". Every example below runs on a demo folder from the ",[267,268,272],"a",{"href":269,"rel":270},"https:\u002F\u002Fgithub.com\u002Fkucherenko\u002Fjscpd\u002Ftree\u002Fmaster\u002Ffixtures",[271],"nofollow","fixtures of the repository",", so you can repeat it on your machine.",[275,276,278],"h2",{"id":277},"functions-that-look-alike-in-15-languages","Functions that look alike, in 15 languages",[254,280,281,282,284],{},"A copied function rarely stays a copy. Someone renames the variables, changes a constant, adds a log line, and the token passes of jscpd lose it, they need a long run of tokens that repeats. ",[258,283,260],{}," is for this case. It compares whole functions by the structure of their syntax trees. jscpd normalizes every function first, the names of the functions it calls and its operators stay, local names, field names and literals become markers. Then two functions score the share of subtrees they have in common, 1.0 is the same structure, 0.8 is the default bar.",[254,286,287],{},"Up to 5.4.0 this worked for JavaScript and TypeScript only, by the sequence of node types, and the score was loose. Now it reads JavaScript, TypeScript, Python, Java, Kotlin, Scala, C#, Go, Rust, C, C++, PHP, Ruby, Swift and Clojure, plus the code blocks of Markdown files and the scripts of Vue, Svelte and Astro components.",[254,289,290],{},"Two Python functions, one prices a parcel, the other prices a policy. Every name and every literal differs, so the default run finds nothing, and the structure is the same:",[292,293,298],"pre",{"className":294,"code":295,"language":296,"meta":297,"style":297},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","jscpd fixtures\u002Fsimilarity-demo\u002Frenamed\n# Found 0 clones.\njscpd fixtures\u002Fsimilarity-demo\u002Frenamed --similarity\n# Clone found (python, similar (ast) ~1.00)\n#  - insurance.py [1:1 - 8:26] (8 lines, 62 tokens)\n#    shipping.py [1:1 - 8:29]\n# Found 1 clones.\n","bash","",[258,299,300,313,320,331,337,343,349],{"__ignoreMap":297},[301,302,305,309],"span",{"class":303,"line":304},"line",1,[301,306,308],{"class":307},"sBMFI","jscpd",[301,310,312],{"class":311},"sfazB"," fixtures\u002Fsimilarity-demo\u002Frenamed\n",[301,314,316],{"class":303,"line":315},2,[301,317,319],{"class":318},"sHwdD","# Found 0 clones.\n",[301,321,323,325,328],{"class":303,"line":322},3,[301,324,308],{"class":307},[301,326,327],{"class":311}," fixtures\u002Fsimilarity-demo\u002Frenamed",[301,329,330],{"class":311}," --similarity\n",[301,332,334],{"class":303,"line":333},4,[301,335,336],{"class":318},"# Clone found (python, similar (ast) ~1.00)\n",[301,338,340],{"class":303,"line":339},5,[301,341,342],{"class":318},"#  - insurance.py [1:1 - 8:26] (8 lines, 62 tokens)\n",[301,344,346],{"class":303,"line":345},6,[301,347,348],{"class":318},"#    shipping.py [1:1 - 8:29]\n",[301,350,352],{"class":303,"line":351},7,[301,353,354],{"class":318},"# Found 1 clones.\n",[254,356,357,358,361,362,365,366,369],{},"An edit costs a few points. ",[258,359,360],{},"refunds.ts"," is ",[258,363,364],{},"orders.ts"," with new names and one ",[258,367,368],{},"trace(refund.id)"," call added inside the loop, the pair scores 0.85:",[292,371,373],{"className":294,"code":372,"language":296,"meta":297,"style":297},"jscpd fixtures\u002Fsimilarity-demo\u002Fedited --similarity\n# Clone found (typescript, similar (ast) ~0.85)\n#  - orders.ts [1:8 - 14:2] (14 lines, 121 tokens)\n#    refunds.ts [1:8 - 15:2]\n# Found 1 clones.\n",[258,374,375,384,389,394,399],{"__ignoreMap":297},[301,376,377,379,382],{"class":303,"line":304},[301,378,308],{"class":307},[301,380,381],{"class":311}," fixtures\u002Fsimilarity-demo\u002Fedited",[301,383,330],{"class":311},[301,385,386],{"class":303,"line":315},[301,387,388],{"class":318},"# Clone found (typescript, similar (ast) ~0.85)\n",[301,390,391],{"class":303,"line":322},[301,392,393],{"class":318},"#  - orders.ts [1:8 - 14:2] (14 lines, 121 tokens)\n",[301,395,396],{"class":303,"line":333},[301,397,398],{"class":318},"#    refunds.ts [1:8 - 15:2]\n",[301,400,401],{"class":303,"line":339},[301,402,354],{"class":318},[254,404,405],{},"Called names count. Two functions with the same shape of statements that call different methods score 0.66, under the default, and show up only when you ask for 0.6:",[292,407,409],{"className":294,"code":408,"language":296,"meta":297,"style":297},"jscpd fixtures\u002Fsimilarity-demo\u002Fcalls --similarity\n# Found 0 clones.\njscpd fixtures\u002Fsimilarity-demo\u002Fcalls --similarity 0.6\n# Clone found (python, similar (ast) ~0.66)\n#  - accounts.py [1:1 - 8:19] (8 lines, 62 tokens)\n#    devices.py [1:1 - 8:18]\n# Found 1 clones.\n",[258,410,411,420,424,437,442,447,452],{"__ignoreMap":297},[301,412,413,415,418],{"class":303,"line":304},[301,414,308],{"class":307},[301,416,417],{"class":311}," fixtures\u002Fsimilarity-demo\u002Fcalls",[301,419,330],{"class":311},[301,421,422],{"class":303,"line":315},[301,423,319],{"class":318},[301,425,426,428,430,433],{"class":303,"line":322},[301,427,308],{"class":307},[301,429,417],{"class":311},[301,431,432],{"class":311}," --similarity",[301,434,436],{"class":435},"sbssI"," 0.6\n",[301,438,439],{"class":303,"line":333},[301,440,441],{"class":318},"# Clone found (python, similar (ast) ~0.66)\n",[301,443,444],{"class":303,"line":339},[301,445,446],{"class":318},"#  - accounts.py [1:1 - 8:19] (8 lines, 62 tokens)\n",[301,448,449],{"class":303,"line":345},[301,450,451],{"class":318},"#    devices.py [1:1 - 8:18]\n",[301,453,454],{"class":303,"line":351},[301,455,354],{"class":318},[254,457,458,459,462,463,466],{},"The search is exact, it finds every pair at or above the ratio. An index of the fingerprints spares it comparing every function with every other one. On 100 open-source repositories it found the same 58,405 pairs as a brute-force run of the same method, in 65 seconds on one thread, the brute-force run took 498. Test files stay out, ",[258,460,461],{},"--min-nodes"," (20 by default) skips the tiny functions, and ",[258,464,465],{},"--similarity 1"," reports the functions with exactly the same structure.",[254,468,469,470,473],{},"Two things to know after the update. The scores differ from 5.4.0, so a threshold or a baseline set on the old scores needs a refresh. And there is a new reporter, ",[258,471,472],{},"-r edn",", which writes every pair with its score and the size of both trees, most similar first:",[292,475,477],{"className":294,"code":476,"language":296,"meta":297,"style":297},"jscpd fixtures\u002Fsimilarity-demo\u002Frenamed --similarity -r edn -o report\n# {:candidates [\n#  {:score 1.0\n#   :language \"python\"\n#   :left {:file \"insurance.py\", :start-line 1, :end-line 8}\n#   :right {:file \"shipping.py\", :start-line 1, :end-line 8}\n#   :left-nodes 112\n#   :right-nodes 112}\n# ]\n#  :clones []}\n",[258,478,479,499,504,509,514,519,524,529,535,541],{"__ignoreMap":297},[301,480,481,483,485,487,490,493,496],{"class":303,"line":304},[301,482,308],{"class":307},[301,484,327],{"class":311},[301,486,432],{"class":311},[301,488,489],{"class":311}," -r",[301,491,492],{"class":311}," edn",[301,494,495],{"class":311}," -o",[301,497,498],{"class":311}," report\n",[301,500,501],{"class":303,"line":315},[301,502,503],{"class":318},"# {:candidates [\n",[301,505,506],{"class":303,"line":322},[301,507,508],{"class":318},"#  {:score 1.0\n",[301,510,511],{"class":303,"line":333},[301,512,513],{"class":318},"#   :language \"python\"\n",[301,515,516],{"class":303,"line":339},[301,517,518],{"class":318},"#   :left {:file \"insurance.py\", :start-line 1, :end-line 8}\n",[301,520,521],{"class":303,"line":345},[301,522,523],{"class":318},"#   :right {:file \"shipping.py\", :start-line 1, :end-line 8}\n",[301,525,526],{"class":303,"line":351},[301,527,528],{"class":318},"#   :left-nodes 112\n",[301,530,532],{"class":303,"line":531},8,[301,533,534],{"class":318},"#   :right-nodes 112}\n",[301,536,538],{"class":303,"line":537},9,[301,539,540],{"class":318},"# ]\n",[301,542,544],{"class":303,"line":543},10,[301,545,546],{"class":318},"#  :clones []}\n",[254,548,549,550,552],{},"The guide has the whole method, with a table of what one edit does to the score - ",[267,551,95],{"href":96},".",[275,554,556],{"id":555},"only-the-clones-of-the-files-you-changed","Only the clones of the files you changed",[254,558,559,561,562,565,566,569,570,573],{},[258,560,264],{}," takes the changed files from ",[258,563,564],{},"git status",", scans everything, and reports a clone when one of its fragments is in a changed file. The first run saves the clones of HEAD to ",[258,567,568],{},".jscpd-baseline.json",", and a clone HEAD did not have is marked ",[258,571,572],{},"[NEW]",". After a commit the baseline is built again on its own.",[254,575,576,577,580,581,584],{},"In a repository where ",[258,578,579],{},"queue.js"," is unchanged and a new ",[258,582,583],{},"ring.js"," copies its class:",[292,586,588],{"className":294,"code":587,"language":296,"meta":297,"style":297},"jscpd --changed src --no-colors --no-tips\n# Clone found (javascript) [NEW]\n#  - queue.js [1:20 - 17:2] (17 lines, 112 tokens)\n#    ring.js [1:19 - 17:2]\n# Found 1 clones (1 new).\n",[258,589,590,606,611,616,621],{"__ignoreMap":297},[301,591,592,594,597,600,603],{"class":303,"line":304},[301,593,308],{"class":307},[301,595,596],{"class":311}," --changed",[301,598,599],{"class":311}," src",[301,601,602],{"class":311}," --no-colors",[301,604,605],{"class":311}," --no-tips\n",[301,607,608],{"class":303,"line":315},[301,609,610],{"class":318},"# Clone found (javascript) [NEW]\n",[301,612,613],{"class":303,"line":322},[301,614,615],{"class":318},"#  - queue.js [1:20 - 17:2] (17 lines, 112 tokens)\n",[301,617,618],{"class":303,"line":333},[301,619,620],{"class":318},"#    ring.js [1:19 - 17:2]\n",[301,622,623],{"class":303,"line":339},[301,624,625],{"class":318},"# Found 1 clones (1 new).\n",[254,627,628,629,632,633,636,637,640,641,552],{},"With ",[258,630,631],{},"--fail-on-new-clones 0"," this is a pre-commit gate that fails on the clone you are about to add and keeps quiet about the old ones. ",[258,634,635],{},"--changed-only"," scans the changed files alone, it is faster and sees only the clones between them. The hook setup is in the ",[267,638,639],{"href":41},"pre-commit guide",", and the demo repository is built step by step in ",[267,642,645],{"href":643,"rel":644},"https:\u002F\u002Fgithub.com\u002Fkucherenko\u002Fjscpd\u002Ftree\u002Fmaster\u002Ffixtures\u002Fchanged-demo",[271],"fixtures\u002Fchanged-demo",[275,647,649],{"id":648},"jscpd-in-the-editor","jscpd in the editor",[254,651,652,655,656,659,660,663],{},[258,653,654],{},"jscpd --lsp"," starts jscpd as a language server. The editor sends the text as you type, after 300 ms of quiet the server tokenizes the file again from the buffer and searches its pool, and the findings come back as diagnostics that follow the text, saved or not. Clones are on by default, similar functions, semantic clones, dead code and complexity are switched on in the ",[258,657,658],{},"lsp"," section of ",[258,661,662],{},".jscpd.json"," or from the editor settings.",[254,665,666],{},"This is what the demo project publishes for one file, printed through headless Neovim:",[292,668,673],{"className":669,"code":671,"language":672,"meta":297},[670],"language-text","4 jscpd\u002Fduplicate-code Duplicated in src\u002Fholds.js:4-13 (80 tokens)\n13 jscpd\u002Fsimilar-function Same structure as the function at src\u002Fholds.js:13-20 (1.00)\n22 jscpd\u002Fcomplex-function Complexity 20 in loanStatus, over the limit of 15\n","text",[258,674,671],{"__ignoreMap":297},[254,676,677,678,681,682,685,686,552],{},"A clone comes with two code actions, go to the other copy, and ignore this clone, which wraps the fragment in ",[258,679,680],{},"jscpd:ignore-start"," and ",[258,683,684],{},"jscpd:ignore-end",". There is a VS Code extension and a JetBrains plugin, both start the server for you and offer to download the binary when it is not on the PATH. Neovim, Helix, Sublime Text and Emacs need a few lines of config, they are in the ",[267,687,688],{"href":66},"editors guide",[275,690,692],{"id":691},"how-far-a-port-has-come","How far a port has come",[254,694,695,698],{},[258,696,697],{},"jscpd --compare source target"," is for a port from one language to another, or for two implementations of one app. It pairs the functions of two folders with the semantic model and reports which functions of each side have a counterpart on the other. The demo is a billing module halfway through a port from Python to TypeScript:",[292,700,703],{"className":701,"code":702,"language":672,"meta":297},[670],"Code\n 71% 5 of 7 functions in python have a counterpart in typescript\n 80% 4 of 5 functions in typescript have a counterpart in python\n\nPaired under other names (1):\n  python                        typescript              similarity\n  billing.py:28 tax_for_region  billing.ts:27 salesTax  0.87 high\n\nOnly in python (2):\n  billing.py (1)\n    46  due_date                6 lines\n  shipping.py (1)\n    18  estimate_delivery_days  8 lines\n\nOnly in typescript (1):\n  billing.ts (1)\n    39  toCurrency  8 lines\n",[258,704,702],{"__ignoreMap":297},[254,706,707,708,711,712,715,716,681,719,722,723,726,727,552],{},"Tests and code are measured apart, a test pairs only with a test. ",[258,709,710],{},"-r html"," draws both sides as dependency graphs with the pairs bridging them. It needs the embedding model, ",[258,713,714],{},"jscpd --semantic-download"," gets it once, 548 MB. It is experimental. On the Java and Python versions of QR-Code-generator it paired 30 of 41 Java functions with no wrong pair. Two agent skills go with it, ",[258,717,718],{},"compare-codebases",[258,720,721],{},"code-migration",", ",[258,724,725],{},"npx skills add kucherenko\u002Fjscpd --skill code-migration"," installs one. The details are in the ",[267,728,729],{"href":86},"compare guide",[275,731,733],{"id":732},"smaller-things","Smaller things",[735,736,737,753,767,777,805,821],"ul",{},[738,739,740,743,744,747,748,552],"li",{},[258,741,742],{},"--report-name"," sets the base name of the report files, so two runs into one folder no longer overwrite each other, and the GitHub Action takes it as ",[258,745,746],{},"report-name",". Thank you ",[267,749,752],{"href":750,"rel":751},"https:\u002F\u002Fgithub.com\u002FMannXo",[271],"@MannXo",[738,754,755,756,759,760,763,764,552],{},"The MCP server takes ",[258,757,758],{},"kinds"," in every tool and has ",[258,761,762],{},"compare_folders",", see the ",[267,765,766],{"href":61},"agents guide",[738,768,769,770,772,773,776],{},"Pairs from ",[258,771,260],{}," have a SARIF rule of their own, ",[258,774,775],{},"jscpd\u002Fsimilar-function",". GitHub code scanning matches alerts by rule, so the first upload after the update closes the old alerts and opens the same findings under the new rule.",[738,778,779,681,782,785,786,789,790,793,794,799,800,552],{},[258,780,781],{},"--semantic",[258,783,784],{},"--compare"," found functions named ",[258,787,788],{},"if"," in C, C++ and C#, a branch after ",[258,791,792],{},"#if"," was read as a function. Reported by ",[267,795,798],{"href":796,"rel":797},"https:\u002F\u002Fgithub.com\u002FMysterionRise",[271],"@MysterionRise",", fixed by ",[267,801,804],{"href":802,"rel":803},"https:\u002F\u002Fgithub.com\u002Fmvanhorn",[271],"@mvanhorn",[738,806,807,810,811,814,815,820],{},[258,808,809],{},"--dead-code"," reported an import used only in a TSDoc ",[258,812,813],{},"{@link}"," as unused. The readers of a ",[267,816,819],{"href":817,"rel":818},"https:\u002F\u002Fdou.ua\u002Fforums\u002Ftopic\u002F62369\u002F",[271],"DOU.ua thread"," caught it.",[738,822,823],{},"git commands run from a hook acted on the hook's repository, that is fixed, and an API key echoed in an embeddings error is redacted now.",[275,825,827],{"id":826},"update","Update",[292,829,831],{"className":294,"code":830,"language":296,"meta":297,"style":297},"curl -fsSL https:\u002F\u002Fjscpd.dev\u002Finstall.sh | bash\n# or\nnpm i -g jscpd@5\n",[258,832,833,851,856],{"__ignoreMap":297},[301,834,835,838,841,844,848],{"class":303,"line":304},[301,836,837],{"class":307},"curl",[301,839,840],{"class":311}," -fsSL",[301,842,843],{"class":311}," https:\u002F\u002Fjscpd.dev\u002Finstall.sh",[301,845,847],{"class":846},"sMK4o"," |",[301,849,850],{"class":307}," bash\n",[301,852,853],{"class":303,"line":315},[301,854,855],{"class":318},"# or\n",[301,857,858,861,864,867],{"class":303,"line":322},[301,859,860],{"class":307},"npm",[301,862,863],{"class":311}," i",[301,865,866],{"class":311}," -g",[301,868,869],{"class":311}," jscpd@5\n",[254,871,872,873,876,877,882,883,885,886,889,890,892],{},"The full list is in the ",[267,874,875],{"href":196},"changelog",". Most of 5.4.1 started as issues from ",[267,878,881],{"href":879,"rel":880},"https:\u002F\u002Fgithub.com\u002Fpygarap",[271],"@pygarap",", the structural ",[258,884,260],{},", the ",[258,887,888],{},"edn"," reporter and ",[258,891,264],{}," are his ideas. Thank you!",[894,895,896],"style",{},"html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}",{"title":297,"searchDepth":315,"depth":315,"links":898},[899,900,901,902,903,904],{"id":277,"depth":315,"text":278},{"id":555,"depth":315,"text":556},{"id":648,"depth":315,"text":649},{"id":691,"depth":315,"text":692},{"id":732,"depth":315,"text":733},{"id":826,"depth":315,"text":827},"2026-10-11","Similar functions by structure in 15 languages, clones of the files you are changing, jscpd inside the editor, and a progress number for a port. With commands and real output.","md",{},"\u002Fblog\u002Fjscpd-5-4",{"title":247,"description":911},"jscpd 5.4.0 and 5.4.1 explained with examples, similar functions in 15 languages, --changed, the language server, --compare and the edn reporter.","blog\u002Fjscpd-5-4",[914],"release","IXmmS0Ef_wH3SdrL6sp2zGxxnq1LZMpPEJzKzhakqGw",1791733828087]