Skip to content
Development
Skill

/migrate-static-to-wrapper

ALWAYS USE when asked to migrate, replace, or make testable existing C# static calls with a named wrapper or built-in abstraction: DateTime.UtcNow/Now or DateTimeOffset.UtcNow to TimeProvider/IClock, File.* to IFileSystem or an existing store such as ITextFileStore, and

From plugin
dotnet-skills
499200 skills17 agents

Claude Code

$ npx -y skills add managedcode/dotnet-skills --skill migrate-static-to-wrapper --agent claude-code

SKILL.md

migrate-static-to-wrapper.SKILL.md
name: migrate-static-to-wrapper
description: >
  ALWAYS USE when asked to migrate, replace, or make testable existing C# static
  calls with a named wrapper or built-in abstraction: DateTime.UtcNow/Now or
  DateTimeOffset.UtcNow to TimeProvider/IClock, File.* to IFileSystem or an
  existing store such as ITextFileStore, and Environment.* to an existing reader
  such as IEnvironmentReader. Covers scoped files/projects, constructor
  injection, replacing temp-file or process-environment tests with fakes,
  "already registered" abstractions, and static classes whose callers/signatures
  must stay unchanged. Preserves DateTimeKind and call count. DO NOT USE for finding
  statics (detect-static-dependencies), choosing/designing a new wrapper
  (generate-testability-wrappers), behavior tests with no chosen seam
  (testability-obstacle), or test-framework migration.
license: MIT

Migrate Static to Wrapper

Perform mechanical, codemod-style replacement of static dependency call sites with calls to injected wrapper interfaces or built-in abstractions. Operates on a bounded scope (single file, project, or namespace) so migrations can be done incrementally.

When to Use

  • After wrappers have been generated (via `generate-testability-wrappers`) or built-in abstractions identified
  • Migrating `DateTime.UtcNow` → `TimeProvider.GetUtcNow()` across a project
  • Migrating `File.*` → `IFileSystem.File.*` across a namespace
  • Adding constructor injection for the new abstraction to affected classes
  • Making a `static` utility class testable by adding an ambient seam (Step 3) while its existing call sites keep

compiling unchanged

  • Incremental migration: one project or namespace at a time
  • Updating affected tests with fakes when the requested migration names the

replacement abstraction

When Not to Use

  • No wrapper or abstraction exists yet and one must be designed from scratch (use `generate-testability-wrappers` first).

A built-in abstraction such as `TimeProvider` or `IFileSystem` always counts as existing.

  • The user wants to detect statics, not migrate them (use `detect-static-dependencies`)
  • Migrating between test frameworks (use the appropriate migration skill)
  • The user primarily asks for a deterministic behavior test and has not selected

the production seam (use `testability-obstacle`)

> A class that is `static`, or a project with no DI container, is **not** a reason to skip this skill — that is exactly > what the ambient seam in Step 3 is for. Use it whenever the call sites must keep compiling unchanged.

Inputs

| Input | Required | Description | |-------|----------|-------------| | Static pattern | No | Infer from the request and discovered call sites (e.g., `DateTime.UtcNow`, `File.ReadAllText`) | | Replacement abstraction | No | Infer from the request and existing project abstractions; stop only when no named/existing abstraction is available | | Scope | No | Infer from the requested file/project/namespace, otherwise discover the narrowest relevant workspace scope | | Injection strategy | No | `constructor` (default), `primary-constructor`, or `ambient` |

Workflow

Non-negotiable migration boundaries

  • **Missing abstraction means stop.** If the named interface/package is absent and

the request only authorizes call-site replacement, do not add a package, invent a local lookalike interface, or edit production code. Report the exact missing prerequisite and the authorization needed to continue.

  • **One source read stays one replacement read.** Do not hoist or coalesce calls,

even when sharing a captured timestamp looks cleaner.

  • **The requested scope is exhaustive and exclusive.** Replace every named call

in scope and no adjacent member or file.

  • **Repository-backed requests require repository work.** Start by discovering

files from the current workspace. Do not claim the repository is unavailable or ask the user for a path or file contents until workspace-relative discovery found no target. Do not say work was implemented unless the diff proves it.

  • **Discovered workspace files must be completed in this turn when permitted.**

Use a host-native shell reader (`sed`/`cat` or `Get-Content`) only after a confirmed reader availability, transport, or path-normalization failure and only after verifying the canonical path remains inside the current workspace. Stop on content-exclusion, permission/policy, workspace-boundary, or unknown read failures. Use a shell edit fallback only for a confirmed editor availability, transport, or path-normalization failure, never for a stale context, concurrent change, permission/policy denial, or path-boundary error. Before fallback, resolve the canonical path inside the current workspace, freshly read the file, and require an anchored replacement with the expected old text and exact match count; abort if either changed. Then re-open the file, inspect the diff, and validate. Do not ask the user to paste a readable discovered file or report a proposed patch as completed work.

Step 1: Verify prerequisites

Before modifying any code:

1. **Confirm the wrapper/abstraction exists**: Check that the interface or built-in abstraction is available in the project. For `TimeProvider`, verify the target framework is .NET 8+ or `Microsoft.Bcl.TimeProvider` is referenced. For `System.IO.Abstractions`, verify the NuGet package is referenced. A package that could provide an abstraction is not the same as an abstraction already available to this project.

2. **Trace production composition**: Check `Program.cs`, `Startup.cs`, factories, and manual construction sites before editing. An explicit migration to an available seam includes the minimal constructor/caller/registration changes needed to keep that scope runnable, unless the user forbids those edits. Register `TimeProvider.System` in an existing DI host when the new dependency is not already registered; update manual callers instea

Read more
@managedcode
Ships withdotnet-skills

Stop explaining .NET to your AI. Start building. We've all been there: asking Claude to use Entity Framework, only to get EF6 patterns in a .NET 8 project. Explaining to Copilot that Blazor Server and Blazor WebAssembly aren't the same thing.

499200 skills17 agents
Get the whole plugin

Stars on GitHub

Momentum

stars in total
499
Star history of managedcode/dotnet-skills on GitHub, a chart by star-history.com

Other skills on dotnet-skills.

aspnet-core

aspnet-core

Build, debug, modernize, or review ASP.NET Core applications with correct hosting, middleware, security, configuration, logging,…

aspire

aspire

Build, upgrade, and operate Aspire 13.5.x C# or TypeScript application hosts with the current CLI, AppHost, ServiceDefaults,…

avalonia

avalonia

Build and debug Avalonia 11/12 .NET applications with XAML, compiled bindings, MVVM, custom controls, desktop lifetimes, and…

azure-functions

azure-functions

Build, review, or migrate Azure Functions in .NET with correct execution model, isolated worker setup, bindings, DI, and Durable…

blazor

blazor

Build and review Blazor applications across server, WebAssembly, web app, and hybrid scenarios with correct component design,…

More skills from other plugins.

brainstorming
Auto-invoked

brainstorming

You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…

@obra@obraInstall Skill
ask-matt

ask-matt

Ask which skill or flow fits your situation. A router over the skills in this repo.

skills2w309k
@mattpocock@mattpocockInstall Skill
accessibility
Auto-invoked

accessibility

Design, implement, and audit accessible UI to WCAG 2.2 Level AA across Web, iOS, and Android — semantic ARIA roles and labels,…

ecc2w293k
@affaan-m@affaan-mInstall Skill
compiler-commit

compiler-commit

Use when you want to verify compiler changes and commit with the correct convention. Runs tests, lint, and format, then commits…

react5d252k
@facebook@facebookInstall Skill

Questions about migrate-static-to-wrapper.

Which coding agents can use migrate-static-to-wrapper?
migrate-static-to-wrapper 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.