| name | refactor |
| description | Use when the user asks for a codebase health check, structural cleanup, or
refactor without a specific bug or feature in mind - phrases like "코드
정리해줘", "구조 점검해줘", "리팩토링 리포트 만들어줘", "정리하면 좋을 곳
점검해줘", "clean up this codebase", "refactor report", or "what's messy in
here". Also use when the user wants to know whether dead code, duplicated
logic, missing tests, or unused dependencies exist before deciding what to
fix. This skill investigates and reports first, then only touches code the
user has explicitly approved - it is not for feature work or bug fixes with
a known target (use `implement` for those).
|
| allowed-tools | ["Bash","Read","Grep","Glob","Task","AskUserQuestion","TodoWrite"] |
refactor
Use this skill to find and fix structural problems in a codebase - files doing
too many jobs, dead code, duplicated logic, misplaced files, missing tests on
core flows, and dependencies that duplicate what's already installed or that
nobody imports anymore.
This skill never changes behavior. Its entire value comes from the promise
that after it runs, the app does exactly what it did before, just organized
better. Every step below exists to protect that promise: investigate before
touching anything, prove each dead-code claim before reporting it, get
explicit sign-off before editing, and verify after every single change.
refactor is the structural-cleanup counterpart to implement. implement
builds toward a spec; refactor improves what already exists without changing
what it does. If the user already knows what's broken and wants it fixed, that
is implement's job, not this skill's.
Before You Start
Detect the project's toolchain instead of assuming one. Read package.json
(or the equivalent manifest for the language in use - Cargo.toml,
pyproject.toml, go.mod, etc.) for the actual scripts: the test runner, the
type checker, the linter, the build command. Use whatever the project defines
- don't guess a generic
npm test if the project's real command is something
else. If a command is aliased or wrapped (the way this repo warns npm can be
shell-aliased to pnpm), resolve the real binary rather than trust the alias.
Run the full check suite once before touching anything, so you know the
baseline: which checks currently pass, and which failures (if any) predate you.
You will re-run the same suite after every change - a check that was already
red before you started is not something this skill needs to fix, but it is
worth naming to the user rather than silently working around.
Check git status --short. If the working tree is not clean, tell the user
what's there and ask before proceeding - the changes you find might be their
in-progress work, and you should never bury it under a refactor commit.
Phase 1: Investigate
Investigate three areas in parallel using the Task tool (Explore-type
subagents for read-only search work). Running them concurrently matters here:
each area is a genuinely independent question, and a single pass trying to
hold "which files are too big" and "which packages are unused" in the same
context tends to shortchange one or the other.
Structure
For each file that looks large or old enough to have accumulated multiple
concerns (a rough heuristic: 250-300+ lines, or a name that no longer matches
what's inside), ask: does this file mix concerns that don't need to travel
together (rendering + data fetching + business logic + validation all in
one), or is it one big cohesive thing (a large form that only does form
logic)? Size alone is not the signal - a 300-line file with one job is fine, a
150-line file juggling three unrelated responsibilities is not.
Look for:
- files whose name or directory no longer matches their contents
- logic that appears near-identically in two or more places (not just similar
shape - actually duplicated behavior that would need to change in both
places if the rule changed)
- exports (functions, components, types) that nothing else in the codebase
imports
Tests
Run the existing test suite and record what actually passes right now - don't
assume from the file list. Then identify which core user-facing flows (auth,
payment, data mutation, the primary thing this app does) have no test
coverage at all, versus which untested files are peripheral (SEO metadata,
static content) where a gap matters much less.
When you find an existing test file, read it to see what pattern it already
uses (pure-function unit tests vs. component tests with mocked dependencies) -
new tests you propose later should extend an established pattern rather than
introduce a third style.
Dependencies
Read the manifest's dependency list and grep for each package's actual import
sites in the codebase - do not flag a package as unused from name recognition
alone. A package can be "used" in a way that's easy to miss (a config file
registering a plugin, a single icon import buried in one component), so
search broadly before concluding a dependency has zero call sites.
Separately, look for hand-rolled code that duplicates something an already-
installed dependency already does (a homemade className merger sitting next
to a project that also has clsx installed, for instance) - these are worth
flagging even when the hand-rolled version works fine, because they're a
second thing to maintain where one would do.
Phase 2: Verify Every Dead-Code Claim
Before anything goes in the report, re-confirm every "this is unused" or
"nothing imports this" claim yourself with a direct grep across the whole
repo (excluding build output and dependency directories). Subagent research is
a starting point, not a citation - a claim that later turns out wrong (the
export was actually consumed via a barrel file, a dynamic import, or a string-
based reference) erodes the trust this whole skill depends on. If you cannot
fully verify a claim, say so plainly in the report as a guess rather than
presenting it as confirmed.
Phase 3: Write the Report
Group findings into one prioritized list ordered by how you'll tackle them,
not by which category they came from. Rank primarily by two independent axes:
- Urgency - does this risk an actual bug (e.g., two copies of the same
label mapping that have already drifted apart), or is it purely cosmetic?
- Blast radius - how many other files or call sites would notice if this
changed? A file with one consumer is a much safer edit than a shared
provider seven components depend on.
Do the safe, narrow, high-value items first; save wide-blast-radius items
(shared state, widely-imported modules) for later, after the safer wins have
built confidence and reduced the noise around them. State this reasoning
explicitly in the report - the user is deciding what to approve, and "why
this order" is exactly what lets them make that call quickly.
Report structure
## [N]. [item name]
**Category**: structure | dead-code | duplication | misplaced-file | test-gap | dependency
**Finding**: what's actually there, with file:line references
**Why it matters**: concrete consequence if left alone (or "cosmetic only -
no functional risk" if that's the honest answer)
**Proposed change**: what would move/merge/delete/extract, named explicitly
**Blast radius**: which files/consumers would be touched or need re-checking
**Confidence**: confirmed via grep | inferred, not fully verified
Close the report with the priority table and the one- or two-sentence
rationale for the ordering.
Phase 4: Get Approval
Do not change a single line of code before the user has told you which items
to proceed with. Use AskUserQuestion to get explicit sign-off per item or
per group of items - the tool caps each question at 4 options, so split a
long item list across multiple questions rather than cramming it into one.
Silence is not approval. An item the user didn't respond to is an item you
don't touch this round.
Phase 5: Execute Approved Items Only
Work through approved items one at a time, in the order agreed in the report
(safest/narrowest first). For each item:
- Re-check
git status --short is still clean before starting it (an
earlier item's commit should have left it clean already).
- Make the change. The behavior contract is strict: if the user can observe
it - rendered text, button labels, API responses, computed values - it
must come out identical. If satisfying "structure only" would require a
user-visible change (e.g., two components have genuinely different display
text for the same underlying value), stop and ask rather than deciding
unilaterally which text wins.
- Run the full check suite (typecheck, lint, tests) and fix anything the
change broke before moving on.
- Stage only the files this item actually touched - never a blanket stage-
everything - and commit with a message explaining why the change was made,
not just what changed. Follow the repository's existing commit-message
conventions and attribution rules (many repos, including this one, exclude
AI-tool attribution from commit text - check for that kind of local
convention before writing the message).
- Commit before moving to the next item. Each item gets its own commit -
never batch multiple approved items into one commit, since that makes any
later revert or bisect land on the wrong change.
If mid-execution you discover a change is riskier than the report estimated
(more consumers than grep initially found, a behavior difference that can't be
preserved silently), stop and go back to the user rather than pushing through.
Phase 6: Final Verification
After the last approved item is committed:
- Run the complete check suite once more end to end (typecheck, lint, tests,
and a production build if the project has one) - not just the narrow
checks from each individual item.
- Run
git log --oneline to show the resulting commit sequence and confirm
git status --short is clean.
- Ask the user whether to push, rather than assuming. Some repositories
commit straight to a trunk branch by convention (check for that in the
project's own instructions before assuming either way), but pushing is a
shared-state action and deserves a check-in regardless of what local
convention says about committing.
What This Skill Is Not For
- A known bug with a specific fix in mind - that's a direct fix, not this
workflow.
- Adding a new feature or new user-facing behavior - that's
implement.
- A single obvious one-line cleanup the user already named explicitly - just
make the change; running the full investigate-report-approve pipeline for
one line the user already specified is wasted motion.