migrate-static-to-wrapper
Mechanically replace static dependency call sites with wrapper or built-in abstraction calls across a bounded scope (file, project, or namespace). Performs codemod-style bulk replacement of DateTime.UtcNow to TimeProvider.GetUtcNow(), File.ReadAllText to IFileSystem, and similar transformations. Adds constructor injection parameters and updates DI registration. USE FOR: replace DateTime.UtcNow with TimeProvider, replace DateTime.Now with TimeProvider, migrate static calls to wrapper, bulk replace File.* with IFileSystem, codemod static to injectable, add constructor injection for time provider, mechanical migration of statics, refactor DateTime to TimeProvider, swap static for injected dependency, convert static calls to use abstraction, replace statics in a class, migrate one file to TimeProvider, scoped migration, update call sites. DO NOT USE FOR: detecting statics (use detect-static-dependencies), generating wrappers (use generate-testability-wrappers), migrating between test frameworks.
What this skill does
# 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 - Incremental migration: one project or namespace at a time ## When Not to Use - No wrapper or abstraction exists yet (use `generate-testability-wrappers` first) - The user wants to detect statics, not migrate them (use `detect-static-dependencies`) - The code does not use dependency injection and the user hasn't chosen ambient context - Migrating between test frameworks (use the appropriate migration skill) ## Inputs | Input | Required | Description | |-------|----------|-------------| | Static pattern | Yes | What to replace (e.g., `DateTime.UtcNow`, `File.ReadAllText`) | | Replacement abstraction | Yes | What to use instead (e.g., `TimeProvider`, `IFileSystem`) | | Scope | Yes | File path, project (.csproj), namespace, or directory to migrate | | Injection strategy | No | `constructor` (default), `primary-constructor`, or `ambient` | ## Workflow ### 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. 2. **Confirm DI registration exists**: Check `Program.cs` or `Startup.cs` for the service registration. If missing, add it before proceeding. 3. **Identify all files in scope**: List the `.cs` files that will be modified. Exclude test projects, `obj/`, `bin/`, and generated code. ### Step 2: Plan the migration for each file For each file containing the static pattern, determine: 1. **Which class(es) contain the call sites** — identify the class declarations 2. **Whether the class already has the dependency injected** — check constructors for existing `TimeProvider`, `IFileSystem`, etc. parameters 3. **The replacement expression** for each call site #### Replacement mapping | Category | Original | DI replacement | |----------|----------|----------------| | Time | `DateTime.Now` | `_timeProvider.GetLocalNow().DateTime` | | Time | `DateTime.UtcNow` | `_timeProvider.GetUtcNow().DateTime` | | Time | `DateTime.Today` | `_timeProvider.GetLocalNow().Date` | | Time | `DateTimeOffset.UtcNow` | `_timeProvider.GetUtcNow()` | | File | `File.ReadAllText(path)` | `_fileSystem.File.ReadAllText(path)` | | File | `File.WriteAllText(path, text)` | `_fileSystem.File.WriteAllText(path, text)` | | File | `File.Exists(path)` | `_fileSystem.File.Exists(path)` | | File | `Directory.Exists(path)` | `_fileSystem.Directory.Exists(path)` | | Env | `Environment.GetEnvironmentVariable(name)` | `_env.GetEnvironmentVariable(name)` | | Console | `Console.WriteLine(msg)` | `_console.WriteLine(msg)` | | Process | `Process.Start(info)` | `_processRunner.Start(info)` | Apply the same pattern for other members in each category. ### Step 3: Add constructor injection Add the new dependency following the class's existing pattern: - **Primary constructor** (C# 12+): Add parameter to primary constructor: `public class OrderProcessor(ILogger<OrderProcessor> logger, TimeProvider timeProvider)` - **Traditional constructor**: Add `private readonly` field + constructor parameter, matching the existing field naming convention (`_camelCase` or `m_camelCase`) ### Step 4: Replace call sites Perform each replacement mechanically. For each call site: 1. Replace the static call with the wrapper call 2. Preserve the surrounding code structure (whitespace, comments, chaining) 3. Add required `using` directives if not already present #### Adding using directives | Abstraction | Using directive | |------------|-----------------| | `TimeProvider` | None (in `System` namespace) | | `IFileSystem` | `using System.IO.Abstractions;` | | `IHttpClientFactory` | `using System.Net.Http;` (usually already present) | | Custom wrappers | `using <wrapper namespace>;` | ### Step 5: Update affected test files If test files exist for the migrated classes: 1. **Update constructor calls** — add the new parameter to test class instantiation 2. **Use test doubles**: - `TimeProvider` → `new FakeTimeProvider()` from `Microsoft.Extensions.TimeProvider.Testing` - `IFileSystem` → `new MockFileSystem()` from `System.IO.Abstractions.TestingHelpers` - Custom wrappers → `new Mock<IWrapperName>()` or hand-rolled fake ### Step 6: Build verification After all changes in the current scope: ```bash dotnet build <project.csproj> ``` If the build fails: - **Missing using**: Add the required `using` directive - **Missing NuGet package**: Run `dotnet add package <name>` - **Constructor mismatch in tests**: Update test instantiation (Step 5) - **Ambiguous call**: Fully qualify the wrapper call ### Step 7: Report changes Summarize what was done: ``` ## Migration Summary **Pattern**: DateTime.UtcNow → TimeProvider.GetUtcNow() **Scope**: MyProject/Services/ ### Files Modified (production) | File | Call Sites Replaced | Injection Added | |------|--------------------:|:----------------| | OrderProcessor.cs | 3 | Yes (constructor) | | NotificationService.cs | 1 | Yes (primary ctor) | ### Files Modified (tests) | File | Change | |------|--------| | OrderProcessorTests.cs | Added FakeTimeProvider parameter | ### Remaining (out of scope) - MyProject/Legacy/ — 8 call sites not migrated (different namespace) ``` ## Validation - [ ] All call sites in scope were replaced (none missed) - [ ] Constructor injection added to all affected classes - [ ] Field naming follows existing class conventions - [ ] Required `using` directives added - [ ] Required NuGet packages referenced - [ ] Build succeeds after migration - [ ] Test files updated with appropriate test doubles - [ ] No behavioral changes introduced (wrapper delegates directly to the static) ## Common Pitfalls | Pitfall | Solution | |---------|----------| | Replacing statics in test code | Only replace in production code; tests should use fakes/mocks | | Breaking static classes | Static classes can't have constructors — use ambient context for these | | Missing `FakeTimeProvider` NuGet | Add `Microsoft.Extensions.TimeProvider.Testing` to test project | | Replacing in expression-bodied members without updating return type | `DateTime` → `DateTimeOffset` when using `TimeProvider.GetUtcNow()` — verify type compatibility | | Migrating too much at once | Stick to the defined scope — one project or namespace per run | | Forgetting DI registration | Always verify `Program.cs`/`Startup.cs` has the registration before replacing call sites |
Related in Code Review
gstack
IncludedFast headless browser for QA testing and site dogfooding. Navigate pages, interact with elements, verify state, diff before/after, take annotated screenshots, test responsive layouts, forms, uploads, dialogs, and capture bug evidence. Use when asked to open or test a site, verify a deployment, dogfood a user flow, or file a bug with screenshots. (gstack)
startup-due-diligence
IncludedLegal due diligence review for seed-stage and Series A startups (US, Delaware C-Corp focus). Supports both investor and founder perspectives. Capabilities include: (1) Interactive document review and issue spotting; (2) Document request list generation; (3) Cap table and SAFE/convertible note analysis; (4) Red flag identification with severity ratings; (5) Diligence report generation. TRIGGERS: due diligence, DD, startup investment, cap table review, Series A, seed round, investor diligence, legal review startup, SAFE analysis, convertible note, 409A, founder vesting.
interview-master
IncludedThis skill should be used when the user asks to "generate interview questions", "prepare for interview", "optimize resume", "conduct mock interview", "analyze git commits for resume", "generate resume from code", "review my resume", or mentions interview preparation, career assistance, or extracting project experience from git history. Provides comprehensive interview and career development guidance for both job seekers and interviewers.
fix-issue
IncludedFixes GitHub issues using parallel analysis agents for root cause investigation, code exploration, and regression detection. Reads issue context from gh CLI, searches codebase and memory for related patterns, generates a fix with tests, and links the resolution back to the issue via PR. Includes prevention analysis to avoid recurrence. Use when debugging errors, resolving regressions, fixing bugs, or triaging issues.
sf-apex
IncludedGenerates and reviews Salesforce Apex code with 150-point scoring. TRIGGER when: user writes, reviews, or fixes Apex classes, triggers, test classes, batch/queueable/schedulable jobs, or touches .cls/.trigger files. DO NOT TRIGGER when: LWC JavaScript (use sf-lwc), Flow XML (use sf-flow), SOQL-only queries (use sf-soql), or non-Salesforce code.
swift-development
IncludedComprehensive Swift development for building, testing, and deploying iOS/macOS applications. Use when Claude needs to: (1) Build Swift packages or Xcode projects from command line, (2) Run tests with XCTest or Swift Testing framework, (3) Manage iOS simulators with simctl, (4) Handle code signing, provisioning profiles, and app distribution, (5) Format or lint Swift code with SwiftFormat/SwiftLint, (6) Work with Swift Package Manager (SPM), (7) Implement Swift 6 concurrency patterns (async/await, actors, Sendable), (8) Create SwiftUI views with MVVM architecture, (9) Set up Core Data or SwiftData persistence, or any other Swift/iOS/macOS development tasks.