/csharp-refactoring
Safely refactors C#/.NET code without changing behavior. USE FOR any request to refactor, rename, move, extract, inline, merge, consolidate, deduplicate, split, or modernize C# code, including partial/generated declarations, wrappers, public APIs,
Claude Code
$ npx -y skills add dotnet/skills --skill csharp-refactoring --agent claude-code
Codex
$ npx skills add dotnet/skills --skill csharp-refactoring -a codex -y
Cursor
$ npx skills add dotnet/skills --skill csharp-refactoring -a cursor -y
Gemini CLI
$ npx skills add dotnet/skills --skill csharp-refactoring -a gemini-cli -y
GitHub Copilot
$ npx skills add dotnet/skills --skill csharp-refactoring -a github-copilot -y
OpenCode
$ npx skills add dotnet/skills --skill csharp-refactoring -a opencode -y
Windsurf
$ npx skills add dotnet/skills --skill csharp-refactoring -a windsurf -y
SKILL.md
csharp-refactoring.SKILL.mdname: csharp-refactoring
description: "Safely refactors C#/.NET code without changing behavior. USE FOR any request to refactor, rename, move, extract, inline, merge, consolidate, deduplicate, split, or modernize C# code, even a small extract, inline, or rename, or merging near-identical types; green builds miss drift in #if branches, generated partials, and reflection/config names. When a changed member is visible outside the assembly (PublicAPI.*.txt, InternalsVisibleTo, shipped package), also invoke dotnet-breaking-changes; local/private renames do not need it. Also use when a request calls a feature, bug fix, package/framework upgrade, or public nullability change a refactor and the behavior-changing part must be separated or declined. DO NOT USE FOR ordinary feature or bug fixes not presented as refactoring; standalone upgrades after reclassification (use dotnet-upgrade); adding tests; or formatting-only work."
license: MIT
C# Refactoring (behavior-preserving)
A refactor changes **structure**, never observable **behavior**. Do the edit with binding-aware tools, then confirm behavior held with a build + the relevant tests. Keep the effort proportional to the change: a one-line local rename does not need the ceremony a public multi-targeted change does.
Mandatory gate: classify before validation or editing
Read only enough repository context to classify **each requested operation**. Do this before restoring, building, or making an edit. Classification precedence is:
- If the request explicitly asks for at least one separable behavior-preserving operation, complete
that structural work and defer only the behavior-changing or contract-changing operations. Do not invent or infer structural work to avoid the stop response.
- If the structural and behavior-changing parts cannot be separated, use the whole-request stop
response and state why they are inseparable.
- Otherwise, use the whole-request stop response only when **every** requested operation is outside
behavior-preserving refactoring.
For a whole request that is outside behavior-preserving refactoring:
1. State: `Not a behavior-preserving refactor: <specific reason>.` 2. State: `No files changed.` 3. State: `Next workflow: <workflow>.` Then stop. Do not add manual implementation steps, alternatives, an offer to proceed without the workflow, or a follow-up question — even when that workflow is unavailable.
| Requested as a "refactor" | Classification and action | |---|---| | Framework or NuGet version change | **Upgrade.** Do not edit or validate the upgrade here; hand off to `dotnet-upgrade`. | | New capability, flag, endpoint, tier, or behavior | **Feature.** Do not implement it here; hand off to the repository's feature workflow. | | Threshold, rate, output, or bug-result change | **Behavior change.** Defer it and hand off to the repository's bug-fix or behavior-change workflow; still complete any clearly separable structural operation. | | Tighten or loosen a shipped/public nullable annotation | **Source-contract change.** Leave the declaration and API record unchanged; hand off to the `dotnet-breaking-changes` skill. |
For a mixed request, never stop after classification. Perform the separable structural operation, explicitly defer the behavior/contract change, and never state `No files changed.` after completing structural work. Never modify tests to make an unauthorized behavior change appear preserved.
Work only in the current workspace
Use the current agent workspace as the boundary. If it is a Git checkout, resolve its repository root (`git rev-parse --show-toplevel`) and stay inside it. If Git metadata is absent, treat the current working directory and its subdirectories as the boundary, and use a solution or project named in the request inside it; Git is not a prerequisite for a refactor. Resolve prompt-provided relative paths inside that boundary.
Search and edit only that workspace. Never use filesystem-wide search or select a similarly named clone, another worktree, build output, or unrelated temporary directory because a file also exists there. If a named path is absent, stop and report the mismatch instead of guessing another workspace. If a tool rejects an in-workspace path for a mechanical reason such as path form or unsupported tool root, retry through another in-workspace mechanism. If the rejection is a permission or policy denial, report it instead of working around it. Never search outside the boundary.
Rename / move by bindings, not text
The #1 way a "rename" silently corrupts code is editing textual matches (comments, strings, unrelated overloads) instead of real **bindings**. Find every binding reference first, then edit semantically. Use the strongest tool available: an IDE/Roslyn workspace refactoring, then the C# LSP the [`dotnet` plugin declares](https://github.com/dotnet/skills/blob/main/plugins/dotnet/lsp.json) (`findReferences`, `goToDefinition`, `incomingCalls`, `rename` code action), then analyzer code-fixes / Roslynator, then compiler-validated edits (edit the true bindings, rebuild, let the compiler flag misses). Plain find/replace only when scope is provably tiny and every hit is verified. Include **every** `partial` declaration, and edit the generator input, never generated (`*.g.cs`) output.
For the operation → Roslyn-provider mapping and representative PRs, see [references/operation-catalog.md](references/operation-catalog.md).
Consolidate toward the existing source of truth
When de-duplicating, preserve the ownership direction stated by the code or request. If `B` duplicates an implementation already owned by `A`, keep `A` canonical and make `B` delegate to it; do not invert the dependency merely because either direction compiles. Preserve public compatibility wrappers when the duplicate surface is shipped, and migrate only in-repo callers that are safe to move.
Decisions that change the edit
Use the first matching row instead of applyi
Read more
name: csharp-refactoring description: "Safely refactors C#/.NET code without changing behavior. USE FOR any request to refactor, rename, move, extract, inline, merge, consolidate, deduplicate, split, or modernize C# code, even a small extract, inline, or rename, or merging near-identical types; green builds miss drift in #if branches, generated partials, and reflection/config names. When a changed member is visible outside the assembly (PublicAPI.*.txt, InternalsVisibleTo, shipped package), also invoke dotnet-breaking-changes; local/private renames do not need it. Also use when a request calls a feature, bug fix, package/framework upgrade, or public nullability change a refactor and the behavior-changing part must be separated or declined. DO NOT USE FOR ordinary feature or bug fixes not presented as refactoring; standalone upgrades after reclassification (use dotnet-upgrade); adding tests; or formatting-only work." license: MIT
C# Refactoring (behavior-preserving)
A refactor changes **structure**, never observable **behavior**. Do the edit with binding-aware tools, then confirm behavior held with a build + the relevant tests. Keep the effort proportional to the change: a one-line local rename does not need the ceremony a public multi-targeted change does.
Mandatory gate: classify before validation or editing
Read only enough repository context to classify **each requested operation**. Do this before restoring, building, or making an edit. Classification precedence is:
- If the request explicitly asks for at least one separable behavior-preserving operation, complete
that structural work and defer only the behavior-changing or contract-changing operations. Do not invent or infer structural work to avoid the stop response.
- If the structural and behavior-changing parts cannot be separated, use the whole-request stop
response and state why they are inseparable.
- Otherwise, use the whole-request stop response only when **every** requested operation is outside
behavior-preserving refactoring.
For a whole request that is outside behavior-preserving refactoring:
1. State: `Not a behavior-preserving refactor: <specific reason>.` 2. State: `No files changed.` 3. State: `Next workflow: <workflow>.` Then stop. Do not add manual implementation steps, alternatives, an offer to proceed without the workflow, or a follow-up question — even when that workflow is unavailable.
| Requested as a "refactor" | Classification and action | |---|---| | Framework or NuGet version change | **Upgrade.** Do not edit or validate the upgrade here; hand off to `dotnet-upgrade`. | | New capability, flag, endpoint, tier, or behavior | **Feature.** Do not implement it here; hand off to the repository's feature workflow. | | Threshold, rate, output, or bug-result change | **Behavior change.** Defer it and hand off to the repository's bug-fix or behavior-change workflow; still complete any clearly separable structural operation. | | Tighten or loosen a shipped/public nullable annotation | **Source-contract change.** Leave the declaration and API record unchanged; hand off to the `dotnet-breaking-changes` skill. |
For a mixed request, never stop after classification. Perform the separable structural operation, explicitly defer the behavior/contract change, and never state `No files changed.` after completing structural work. Never modify tests to make an unauthorized behavior change appear preserved.
Work only in the current workspace
Use the current agent workspace as the boundary. If it is a Git checkout, resolve its repository root (`git rev-parse --show-toplevel`) and stay inside it. If Git metadata is absent, treat the current working directory and its subdirectories as the boundary, and use a solution or project named in the request inside it; Git is not a prerequisite for a refactor. Resolve prompt-provided relative paths inside that boundary.
Search and edit only that workspace. Never use filesystem-wide search or select a similarly named clone, another worktree, build output, or unrelated temporary directory because a file also exists there. If a named path is absent, stop and report the mismatch instead of guessing another workspace. If a tool rejects an in-workspace path for a mechanical reason such as path form or unsupported tool root, retry through another in-workspace mechanism. If the rejection is a permission or policy denial, report it instead of working around it. Never search outside the boundary.
Rename / move by bindings, not text
The #1 way a "rename" silently corrupts code is editing textual matches (comments, strings, unrelated overloads) instead of real **bindings**. Find every binding reference first, then edit semantically. Use the strongest tool available: an IDE/Roslyn workspace refactoring, then the C# LSP the [`dotnet` plugin declares](https://github.com/dotnet/skills/blob/main/plugins/dotnet/lsp.json) (`findReferences`, `goToDefinition`, `incomingCalls`, `rename` code action), then analyzer code-fixes / Roslynator, then compiler-validated edits (edit the true bindings, rebuild, let the compiler flag misses). Plain find/replace only when scope is provably tiny and every hit is verified. Include **every** `partial` declaration, and edit the generator input, never generated (`*.g.cs`) output.
For the operation → Roslyn-provider mapping and representative PRs, see [references/operation-catalog.md](references/operation-catalog.md).
Consolidate toward the existing source of truth
When de-duplicating, preserve the ownership direction stated by the code or request. If `B` duplicates an implementation already owned by `A`, keep `A` canonical and make `B` delegate to it; do not invert the dependency merely because either direction compiles. Preserve public compatibility wrappers when the duplicate surface is shipped, and migrate only in-repo callers that are safe to move.
Decisions that change the edit
Use the first matching row instead of applyi
This repository contains the .NET team's curated set of portable skills and host-specific custom agents for coding agents. For information about the Agent Skills standard, see agentskills.io.
Other skills on skills.
More skills from other plugins.
brainstorming
You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…
ask-matt
Ask which skill or flow fits your situation. A router over the skills in this repo.
Questions about csharp-refactoring.
- Which coding agents can use csharp-refactoring?
- csharp-refactoring installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.

