From codebase-analysis
Find copy-pasted code blocks and similar patterns using jscpd. Use when asked to find duplicate code, identify refactoring targets, or check if a utility should be extracted because it appears in multiple places.
How this skill is triggered — by the user, by Claude, or both
Slash command
/codebase-analysis:duplicate-detectionThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Use jscpd to find copy-pasted code blocks using token comparison.
Use jscpd to find copy-pasted code blocks using token comparison.
# Run detection + generate HTML report
pnpm analyze:dupes
# Open the HTML report (side-by-side diff per clone group)
open /tmp/jscpd-report/html/index.html
Create a .jscpd.json file at project root:
{
"threshold": 5,
"reporters": ["html", "console"],
"ignore": [
"**/__tests__/**",
"**/*.test.ts",
"**/*.test.js",
"**/dist/**",
"**/node_modules/**"
],
"gitignore": true,
"minLines": 8,
"minTokens": 50,
"format": ["typescript", "javascript"],
"output": "/tmp/jscpd-report"
}
When a codebase enforces consistent shape across similar files (e.g., action file entry points, component definitions), jscpd will report them as duplicates even though the duplication is intentional.
Example — action file entry points:
// actions/createItem.ts
export const createItemAction: ActionConfig = {
id: "create-item",
label: "Create Item",
schema: createItemSchema,
execute: async (params, api) => { ... }
};
// actions/deleteItem.ts
export const deleteItemAction: ActionConfig = {
id: "delete-item",
label: "Delete Item",
schema: deleteItemSchema,
execute: async (params, api) => { ... }
};
The outer shell is identical. jscpd flags it. But this uniformity is the point — every action follows the same contract, which makes adding new actions predictable and safe. Do not refactor.
When a response type or protocol requires the same fields to be assembled in every consumer, the assembly code will look duplicated.
Example — response builders with the same shape:
// In handlerA
return { processed: results.length, skipped: skipped.length, errors, dryRun };
// In handlerB
return { processed: results.length, skipped: skipped.length, errors, dryRun };
The contents differ per handler — only the shape is shared.
When two code paths implement the same kind of operation but for different cases, jscpd will flag the retry/fallback scaffold as a duplicate.
Example — lookup strategies:
// lookupByCode
async function lookupByCode(code: string): Promise<User | null> {
for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
try {
const result = await api.findByCode(code);
if (result) return result;
} catch (err) {
if (attempt === MAX_RETRIES) throw err;
await delay(RETRY_DELAY);
}
}
return null;
}
// lookupById
async function lookupById(id: string): Promise<User | null> {
for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
try {
const result = await api.findById(id);
if (result) return result;
} catch (err) {
if (attempt === MAX_RETRIES) throw err;
await delay(RETRY_DELAY);
}
}
return null;
}
Investigate before accepting. Ask: should there be a single findUser(identifier, method) function?
When two different feature areas contain the same block of logic, that logic belongs in shared/utils/.
__tests__/ directories and *.test.ts / *.spec.ts filesdist/, build/, lib/)jscpd reports "clone groups" — sets of code blocks that are structurally near-identical based on token comparison. It detects structural similarity even when variable names differ.
Important: jscpd cannot determine intent. It reports what looks similar, not whether it should be different. Every clone group requires manual review.
services/A.ts and services/B.ts share logic, create shared/utils/sharedHelper.ts"ignore" in .jscpd.jsonConfigure jscpd to analyze the app/ directory. Pages and layouts often have similar structure — focus on finding duplicate business logic, not layout patterns.
Components in components/ui/ are intentionally similar — each is a self-contained primitive. Don't try to consolidate them.
jscpd findings gain more meaning when combined with knip results.
jscpd flags two blocks as duplicates + knip flags one of them as an unused export = strong signal of a bypassed utility
jscpd: src/utils/validator.ts:14-28 clones src/handlers/items.ts:67-81
knip: "validateItemInput" — unused export in src/utils/validator.ts
This means: a utility exists for this validation, nobody imports it, and the validation logic was written inline. The fix is to replace the inline code with a call to the utility — not to delete either.
npx claudepluginhub kylebrodeur/codebase-analysis --plugin codebase-analysisDetects code duplicates using jscpd token-level analysis across files, classifies exact/near/structural clones, ranks by impact score (lines x instances), prepares prioritized refactoring plans.
Detects code clones and copy-paste patterns in any language using structural fingerprinting. Surfaces duplicate candidates for AI-assisted review and refactoring.
Detects semantically duplicate functions with different implementations across a codebase, using LLM clustering. Useful before major refactoring, especially for LLM-generated code.