npx skills add ...
npx skills add kucherenko/jscpd --skill codebase-refactoring
Three-part workflow to improve overall codebase health with jscpd — find and fix duplicated code, find and remove or refactor dead code, then find and simplify the largest/most complex files. Starts from --dashboard/--health to prioritize and ends by re-measuring the score.
npx skills add kucherenko/jscpd --skill codebase-refactoring
A guided pass over a codebase's three biggest maintenance costs — duplicated code, dead code, and complexity — using jscpd to find each and a strategy to fix it. Use this skill when asked to "clean up", "improve", "refactor" or "pay down tech debt in" a codebase, rather than to fix one specific bug.
The health score is a weighted mix of three sub-scores, each a share of the
code lines — high is good, 100 is clean. The lowest of the three names where
the codebase actually hurts most; if it isn't the order below, do that
dimension first instead. n/a on a dimension (e.g. dead code n/a) means
jscpd could not measure it — dead-code detection needs JavaScript,
TypeScript or Python to be a real share of the code — skip that step rather
than treating it as clean.
For the full picture behind that score — largest duplicated formats, most
complex files, largest dead-code findings, all in one screen — run npx jscpd --dashboard instead. Its console output (the default) is the only
reporter with that per-file detail; --reporters ai on --dashboard prints
the same one-line summary shown above, not the breakdown, so don't reach for
it expecting more than the score. Use --reporters json on --dashboard if
you need the detail in a parseable form.
The three passes below are otherwise independent and safe to run in any order; duplication → dead code → complexity is the default because fixing duplication first means step 2 isn't wasting effort tracing call sites that a later extract-function pass will move anyway, and clearing dead code before step 3 means the files ranked for simplification are ones actually worth simplifying, not ones about to be deleted.
Find and read each clone, decide whether it is a real copy worth merging, and apply the matching refactoring strategy (extract function, parameterize, extract a shared module or constant, or a base class/template for structural duplication). Full guided workflow, triage rules for renamed/near-miss clones, and worked examples per clone kind:
→ dry-refactoring — run this now, then
come back here for steps 2 and 3. Its own --summary hotspot ranking is the
same one the health score's duplication dimension reads from, so a file it
flags as a duplication hotspot is the same file that dragged the score down.
This is a graph traversal from the project's entry points (package.json
main/bin/exports/scripts, pyproject.toml scripts, framework
conventions, or --entry <glob>), not a reference count — a helper whose
only caller is itself dead is reported too, so deleting one finding can make
another true that wasn't reported yet. Covers JavaScript, TypeScript, JSX,
TSX, Vue, Svelte, Astro and Python.
| Category | Meaning | Usual fix |
|---|---|---|
unused-file | Never imported anywhere, not an entry point | Delete the file |
unused-export | Exported, but no other file imports it | Delete it, or drop the export keyword if something in the same file still uses it |
unused-symbol | A module-private declaration nothing in its own file references | Delete it |
unused-import | Imported but never referenced in the file | Delete the import line |
--reporters ai (add --dead-code-categories to focus on one
category, --include-tests if test-only usage shouldn't count as "used",
--include-entry-exports to also flag an entry file's own unused
exports, normally excluded since its whole surface is the public API)eval/getattr calls,
decorators the analyzer doesn't recognize, a wildcard re-export, the name
showing up only inside a string literal, or a file elsewhere that failed
to parse (see statistics.unparsedFiles in the JSON report). Read the
reason before deleting; --min-confidence (default 60) only sets the
floor for what's reported, it doesn't make a low-confidence finding safe--dead-code after a batch: a file whose only import was the one
you just deleted may now be dead itself — that cascade is expected, keep
going until a clean pass, not just oncecx is a language-aware cyclomatic-complexity estimate — roughly one path
per function plus one per branch (if, loops, &&/||, match/switch
arms, the ternary and Elvis operators, each counted the way the language
actually spells it) — read as a ranking signal, not an exact metric. Prose
and data files (Markdown, JSON, YAML, lock files, …) always score 0; a
large README is not a refactoring target. jscpd's own health score treats a
file with complexity 50 or higher as "complex" (the threshold its
dead code-style half-life curve is calibrated against) — a reasonable
default cutoff for "worth reading" if the summary doesn't make an obvious
one clear.
--summary form, by
size — a huge low-complexity file, e.g. a big data table, is a different
problem than a small high-complexity one)switch/if-chain over a type
or state, or a file that is really several modules glued together--complexity on the touched files to confirm cx actually
dropped — a split that only moves code around without reducing branching
per function didn't fix anything, it just renamed the problemCompare the score and grade to the Step 0 measurement. A dimension that got
worse (duplication crept back in while extracting a shared complexity fix,
say) is a signal to revisit that step, not just note it — re-run the
relevant pass (--summary, --dead-code, --complexity) rather than only
trusting the one aggregate number.
--dashboard first — on a project where only one dimension is
actually bad, spending equal effort on all three wastes time on the two
that are already fine--health-input file (coverage, security scan results) can be layered
onto the same score if the project already tracks those; it doesn't change
anything about this workflow, it just adds more dimensions to Step 0n/a to measured (or the reverse)
because the file mix changed, say so rather than reading the raw score
delta as improvement or regressionnpx jscpd --dead-code --reporters ai <path>basta dead code report — 18 findings across 30 files (22.1% of 272 lines)
Confidence is 0-100; anything below 90 has a listed reason it may be wrong. Verify a finding before deleting the code.
unused-file typescript/src/legacy-export.ts:1:1 confidence=95 typescript/src/legacy-export.ts is never imported and is not an entry point
unused-export typescript/src/invoice.ts:21:17 confidence=85 exported function `renderReceipt` is never imported
unused-symbol typescript/src/invoice.ts:29:10 confidence=90 function `describeTotal` is never used in typescript/src/invoice.ts
unused-import typescript/src/invoice.ts:2:10 confidence=100 `roundToCents` is imported but never usednpx jscpd --complexity --reporters ai --summary-top 10 <path>
# or, ranked alongside duplication and size:
npx jscpd --reporters ai --summary --summary-by complexity <path>Complexity by complexity (6 files, 6 folders):
files (tokens/lines/size/cx):
c/checkout.c 176/32/710/13
rust/status.rs 112/24/481/9
swift/Profile.swift 88/21/446/8