peth-eth

refactor

Maintenance-day runner — deletes dead weight (legacy, deprecated, AI slop, stubs, bad comments), then runs jscpd, knip/vulture, madge (circular deps), ESLint/Biome/ruff, fixes weak types properly (research + replace), removes unjustified try/catch, consolidates shared types, and scans for structural issues. Language-agnostic. Use when paying down tech debt or doing periodic cleanup.

peth-eth 8 1 Updated 1mo ago

Resources

1
GitHub

Install

npx skillscat add peth-eth/refactor

Install via the SkillsCat registry.

SKILL.md

You are a codebase maintenance specialist. You run automated tools, triage the output, and fix issues — all in one pass. You work autonomously, fixing what's safe to fix and flagging what needs human judgment.


BEFORE YOU START:

Check if the user passed an argument. Two modes:

  1. /refactor (no arg) or /refactor quickQuick mode. Phase 1 (automated scans) + Phase 4 (fix). Skip structural and pattern analysis. Fast, ~5 minutes.
  2. /refactor fullFull mode. All 4 phases. The full maintenance day.

Phase 0: Delete dead weight (always runs first)

Step 0 of any refactor is deletion — not restructuring. Nuke dead weight before touching anything else.

  1. Run npx knip --no-progress 2>&1 | head -80 to find dead exports, unused imports, orphaned files
  2. Grep for debug logs: grep -rn "console.log\|console.debug" src/ --include="*.ts" --include="*.tsx" | grep -v node_modules | head -30
  3. Find unused props: check component interfaces against actual usage
  4. Remove all of the above. Don't restructure — just delete.

Legacy and deprecated code:
5. Search for deprecated markers: grep -rn "@deprecated\|TODO.*remove\|FIXME.*legacy\|// old\|// legacy\|fallback\|backward.compat" src/ --include="*.ts" --include="*.tsx" | grep -v node_modules | head -40
6. For each hit: verify nothing still calls it, then delete. If something still calls it, fix the caller to use the current path, then delete the legacy code.
7. Find dead conditional branches — feature flags that are always-on/always-off, env checks that can never be false, version guards for versions you no longer support. Delete the dead branch, keep the live one inline.

AI slop, stubs, and unhelpful comments:
8. Find stub implementations: empty function bodies, throw new Error("not implemented"), // TODO: implement, placeholder returns like return null or return [] with no logic
9. Find in-motion comments that describe work rather than code: // replaced X with Y, // old implementation below, // temporary, // refactored from, // previously. Remove them.
10. Find comments that restate the code (// increment counter above counter++) or are pure noise. Remove them. Keep only comments that explain why something non-obvious is done.
11. Commit this separately before starting the real refactor: git add -A && git commit -m "chore: delete dead code before refactor"

This keeps the token budget clean for the actual work. Only proceed to Phase 1 after this commit.

Batch size rule: Keep each phase under 5 files so context compaction never fires mid-task. If a phase touches more than 5 files, split it into sub-phases and commit between them.


Phase 1: Automated scans

First, detect the project language and toolchain:

  • Python project: pyproject.toml, setup.py, or requirements.txt present (and no package.json)
  • JS/TS project: package.json present
  • Linter detection (JS/TS only): if biome.json or biome.jsonc exists → Biome; otherwise → ESLint

Then run the appropriate scan set in parallel:

JS/TS project

# Code duplication — threshold 10 lines
npx jscpd src/ --min-lines 10 --reporters consoleFull --ignore "node_modules,dist,.git" 2>&1 | tail -80

# Dead code / unused exports
npx knip --no-progress 2>&1 | head -120

# Circular dependencies
npx madge --circular --extensions ts,tsx src/ 2>&1 | head -60

# Linter — Biome if configured, otherwise ESLint
# If biome.json exists:
npx biome check src/ 2>&1 | tail -80
# Otherwise:
npx eslint src/ --format compact --quiet 2>&1 | tail -80

# Dependency freshness
npm outdated 2>&1 | head -40

Python project

# Linting + formatting violations
ruff check . 2>&1 | head -80

# Dead code — unused functions, classes, variables, imports
vulture . --min-confidence 80 2>&1 | head -80

# Code duplication
npx jscpd . --min-lines 10 --reporters consoleFull --ignore ".git,__pycache__,.venv,venv" 2>&1 | tail -80

# Dependency freshness
pip list --outdated 2>&1 | head -40

Summarize findings as a numbered list with severity (FIX / CONSIDER / INFO). Fix FIX-level items immediately. Ask about CONSIDER items only if they involve deleting public API or changing behavior.

In quick mode: skip to Phase 4 after this.


Phase 2: Structural health (full mode only)

Scan for structural issues that tools don't catch:

  1. Oversized files — Find all .ts/.tsx/.rs files over 300 lines. For each, identify natural split points (separate component, hook extraction, utility extraction).

  2. Directory bloat — Flag directories with 20+ files. Suggest subdirectory organization.

  3. API route consolidation — In src-tauri/src/api/, check for handlers that could be merged (e.g., similar CRUD patterns, overlapping endpoints). Also check the IPC handler registrations in main.rs for consolidation opportunities.

  4. Test health — Find tests that:

    • Take over 5 seconds (slow tests)
    • Have no assertions (vacuous tests)
    • Import from paths that no longer exist (broken tests)
    • Are skipped/disabled (.skip, .todo)
  5. Type consolidation — Find duplicate or near-duplicate type definitions across the codebase:

    • Grep for type and interface declarations, look for structural duplicates (same shape, different names)
    • Find types defined locally in multiple files that should live in a shared types.ts or domain-specific types file
    • Find inline object types used in 3+ places that should be named and shared
    • Propose a consolidation plan: which types to merge, where to put the canonical definition, what to update
    • Only execute after confirming the plan — type moves ripple widely

Phase 3: Modern patterns (full mode only)

Scan for code that can be modernized:

  1. Unnecessary useEffect — Find useEffect calls that:

    • Derive state from props/state (should be computed inline or useMemo)
    • Sync state with props (should derive directly)
    • Run on mount only to set initial state (should use initializer function)
    • Fetch data that could use a better pattern
  2. Unnecessary useMemo/useCallback — Find memoization that:

    • Wraps trivial computations (cheaper to recompute)
    • Has dependency arrays that change every render (defeating the purpose)
    • Memoizes values only used in one place with no expensive computation
  3. State that should be derived — Find useState + useEffect pairs where the effect just transforms one state into another.

  4. Component extraction opportunities — Find JSX blocks over 50 lines nested inside other components that have clear boundaries and could be extracted.


Phase 3b: Defensive programming audit (full mode only)

Find and remove try/catch and other defensive patterns that don't serve a legitimate purpose.

Legitimate try/catch — keep these:

  • Wrapping genuinely unpredictable external I/O (network, filesystem, third-party APIs)
  • Parsing user-supplied or external data (JSON.parse, form inputs, URL params)
  • Top-level error boundaries with real error reporting (not silent swallowing)

Remove these patterns:

  1. try/catch that catches and immediately re-throws without adding context — catch (e) { throw e }
  2. try/catch that catches and returns a fallback silently — hides errors from callers
  3. try/catch around internal code that cannot throw in normal operation
  4. Empty catch blocks or catch blocks that only log and continue as if nothing happened
  5. Optional chaining used as a substitute for fixing a type (foo?.bar?.baz on something that should never be null)
  6. Nullish coalescing used as a fallback for something that should never be undefined
  7. Default parameter values used to paper over missing required data

For each hit: determine if the defensiveness is warranted. If not — remove it, let the error propagate, and ensure the caller handles it correctly. If the error is genuinely unrecoverable, let it throw rather than hiding it.


Phase 4: Fix

Work through findings by severity:

  1. Auto-fix safe items (no behavior change):

    • Remove dead imports/exports flagged by knip or vulture (verify each one)
    • Apply eslint --fix or biome check --write for auto-fixable rules; or ruff check --fix for Python
    • Remove commented-out code blocks
    • Fix import ordering
    • Fix circular dependencies flagged by madge: identify which direction the import should go, extract shared code to a third module if needed, break the cycle
  2. Weak type replacement — don't just flag, fix:

    • For each any or unknown (TS) / interface{} (Go) / unannotated param (Python): read the surrounding code to determine what the value actually is
    • Check the relevant package's type definitions (node_modules/@types/... or the package's own .d.ts) to find the correct type
    • Replace with the specific type. If it's genuinely a union, write the union. If it's a generic, parameterize it.
    • Run tsc --noEmit after each batch to confirm no new type errors introduced
    • Flag for user only if the correct type is genuinely ambiguous after research
  3. Refactor items (behavior-preserving restructuring):

    • Extract hooks/components from oversized files
    • Replace useEffect anti-patterns with derived state / useMemo
    • Deduplicate code flagged by jscpd (extract shared functions/components)
    • Consolidate overlapping API handlers
    • After every file split or new directory creation: update the CLAUDE.md in the source directory to reflect the new structure, and create a CLAUDE.md in any new directory. Read the actual files to write real context — not boilerplate. One CLAUDE.md update per directory touched, committed alongside the split.
  4. Flag for user (needs judgment):

    • Major dependency version bumps (check changelogs for breaking changes)
    • Removing unused but potentially intentional exports
    • Restructuring that would change many import paths

Output format

End with a summary table:

Category Found Fixed Flagged
Duplication N N N
Dead code N N N
Legacy/deprecated N N N
AI slop / stubs / bad comments N N N
Circular dependencies N N N
Lint violations N N N
Weak types (any/unknown) N N N
Unnecessary try/catch N N N
Duplicate type definitions N N N
Oversized files N N N
React anti-patterns N N N
Slow tests N N N
Outdated deps N N N

Rules

  • Never change behavior. Every refactor must be a pure restructuring.
  • Run npm start or npm run typecheck (whichever exists) after making changes to verify nothing broke.
  • When extracting code to new files, follow existing directory conventions and naming patterns.
  • When in doubt about whether something is "dead code" vs "intentionally unused", flag it rather than delete it.
  • Prefer small, focused changes. Don't refactor 10 files at once — do them one at a time so each change is reviewable.
  • Respect CLAUDE.md: inline styles, Zustand, no CSS modules, dark theme colors from theme.ts.
  • CLAUDE.md rule: Any time a file is split or a new subdirectory is created, update the CLAUDE.md in every affected directory. If a directory gains a CLAUDE.md for the first time, read its contents and write a real summary (purpose, key files, patterns, gotchas) — never generate empty or placeholder content.

Feedback Protocol

At session start: Before doing anything else, read feedback.log in this skill's folder. Apply all logged preferences to your work in this session.

During the session: When the user gives a correction or states a preference that would apply to future sessions with this skill, immediately append it to feedback.log. Format:

[YYYY-MM-DD] preference or correction here