| name | dry |
| description | Find structural duplicate code (DRY violations) across a Python or JS/TS codebase using normalized AST fingerprinting, report candidates with similarity scores, and refactor them after user approval. Use when user mentions DRY, duplicate code, copy-paste code, structural duplication, or says "dry". |
DRY — Don't Repeat Yourself
What it detects
Two functions can look different in naming and literal values yet share identical structure. This skill finds those structural duplicates by normalising each function's AST — replacing variable names with :symbol, literals with :literal, and preserving only the skeleton (control flow, collection shapes, call patterns). It then compares every pair of functions via Jaccard similarity over their fingerprint sets.
A score of 1.0 means two functions are structurally identical despite different names and values. The default threshold of 0.82 catches candidates close enough to warrant review.
Supported languages: Python, JavaScript, TypeScript (including JSX/TSX).
Workflow
Phase 1 — Scan
Phase 2 — Review
For each candidate pair above the threshold:
Phase 3 — Report
Present a table to the user:
| # | Score | Left | Right | Classification | Proposed action |
Include totals: functions scanned, duplicate pairs found, score distribution.
Ask user to confirm which pairs to refactor (all, subset, or skip).
Phase 4 — Refactor
After user confirms:
.dryignore
The script respects a .dryignore file in the project root (same directory as --base-dir). Syntax is gitignore-like:
# Ignore generated code
generated/
*.gen.ts
# Ignore test fixtures
__fixtures__/
*_fixture.py
# But keep this one
!__fixtures__/important_fixture.py
Blank lines and # comments are ignored. ! negates a previous match.
Key rules
- Never refactor without running tests to confirm nothing broke
- Keep refactoring minimal — only unify what's genuinely duplicated
- If two functions score high but serve intentionally different domains, flag but don't force unification — some duplication is acceptable when coupling would be worse
- Accidental similarity (e.g. two simple CRUD handlers) is not a DRY violation — use judgement
- Default thresholds are conservative; lower
--threshold for broader sweeps, raise it for precision
- The script auto-skips
node_modules, .git, __pycache__, venv, dist, build, and similar directories
Script
The bundled scripts/dry_check.py handles: Python AST parsing (via stdlib ast), JS/TS token-based structural parsing, normalisation, fingerprinting, Jaccard similarity, .dryignore filtering, and text/JSON output. Zero external dependencies. See REFERENCE.md for algorithm details and CLI usage.