Skip to content
Development
Skill

/detect-static-dependencies

Scan C# source files for hard-to-test static dependencies — DateTime.Now/UtcNow, File.*, Directory.*, Environment.*, HttpClient, Console.*, Process.*, and other untestable statics. Produces a ranked report of static call sites by frequency. USE FOR: find untestable statics, scan

From plugin
dotnet-skills
5.1k96 skills16 agents
Install
$ npx -y skills add dotnet/skills --skill detect-static-dependencies --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/detect-static-dependencies

Context preview

The summary Claude sees to decide when to auto-load this skill.

Scan C# source files for hard-to-test static dependencies — DateTime.Now/UtcNow, File.*, Directory.*, Environment.*, HttpClient, Console.*, Process.*, and other untestable statics. Produces a ranked report of static call sites by frequency. USE FOR: find untestable statics, scan

SKILL.md

detect-static-dependencies.SKILL.md
name: detect-static-dependencies
description: >
  Scan C# source files for hard-to-test static dependencies — DateTime.Now/UtcNow,
  File.*, Directory.*, Environment.*, HttpClient, Console.*, Process.*, and other
  untestable statics. Produces a ranked report of static call sites by frequency.
  USE FOR: find untestable statics, scan for static dependencies, testability audit,
  identify hard-to-mock code, find DateTime.Now usage, detect static coupling,
  testability report, static analysis for testability.
  DO NOT USE FOR: generating wrappers (use generate-testability-wrappers),
  migrating code (use migrate-static-to-wrapper), general code review,
  or finding statics that are already behind abstractions.
license: MIT

Detect Static Dependencies

Scan a C# codebase for calls to hard-to-test static APIs and produce a ranked report showing which statics appear most frequently, which files are most affected, and which abstractions already exist in the .NET ecosystem to replace them.

When to Use

  • Auditing a project's testability before adding unit tests
  • Understanding the scope of static coupling in a legacy codebase
  • Prioritizing which statics to wrap first (highest-frequency wins)
  • Creating a migration plan for incremental testability improvements

Response Guidelines

  • Scale the response to the user's request. A question about a specific category (e.g., "find time statics") should focus on that category with file locations and counts, not produce a full report across all categories.
  • When the user provides a specific file or directory path, scan only that scope — do not expand to the entire solution unless asked.
  • The full structured report format in Step 4 is for comprehensive audit requests. For focused questions, return only the relevant subset (e.g., category summary + affected files for the requested category).

When Not to Use

  • The user wants wrappers generated (hand off to `generate-testability-wrappers`)
  • The user wants mechanical migration done (hand off to `migrate-static-to-wrapper`)
  • The statics are already behind interfaces or `TimeProvider`
  • The code is not C# / .NET

Inputs

| Input | Required | Description | |-------|----------|-------------| | Target path | Yes | A file, directory, project (.csproj), or solution (.sln) to scan | | Exclusion patterns | No | Glob patterns to skip (e.g., `**/obj/**`, `**/Migrations/**`) | | Category filter | No | Limit to specific categories: `time`, `filesystem`, `environment`, `network`, `console`, `process` |

Workflow

Step 1: Determine scan scope

Resolve the target to a set of `.cs` files:

  • If a `.cs` file, scan that single file.
  • If a directory, scan all `.cs` files recursively (excluding `obj/`, `bin/`).
  • If a `.csproj`, find its directory and scan `.cs` files within.
  • If a `.sln`, parse it, find all project directories, and scan `.cs` files across all projects.

Always exclude `obj/`, `bin/`, and any user-specified exclusion patterns.

Step 2: Search for static dependency patterns

Scan each file for calls matching these categories:

| Category | Patterns to search for | Recommended replacement | |----------|----------------------|------------------------| | **Time** | `DateTime.Now`, `DateTime.UtcNow`, `DateTime.Today`, `DateTimeOffset.Now`, `DateTimeOffset.UtcNow`, `Task.Delay(`, `new CancellationTokenSource(TimeSpan` | `TimeProvider` (.NET 8+) | | **File System** | `File.ReadAllText(`, `File.WriteAllText(`, `File.Exists(`, `File.Delete(`, `File.Copy(`, `File.Move(`, `Directory.Exists(`, `Directory.CreateDirectory(`, `Directory.GetFiles(`, `Directory.Delete(`, `Path.GetTempPath(`, and instance members that hit the disk (`new FileInfo(...)`, `new DirectoryInfo(...)`, `.LastWriteTimeUtc`, `new StreamReader(path)`) | `IFileSystem` (System.IO.Abstractions NuGet) | | **Randomness / identity** | `new Random(`, `Random.Shared`, `Guid.NewGuid(` | `TimeProvider`-style seam: inject `Random` / an `IGuidProvider` | | **Culture / serialization** | `CultureInfo.CurrentCulture`, `CultureInfo.CurrentUICulture`, `JsonSerializer.Serialize(`, `JsonSerializer.Deserialize(` | Pass culture/options explicitly, or inject a serializer abstraction | | **Environment** | `Environment.GetEnvironmentVariable(`, `Environment.SetEnvironmentVariable(`, `Environment.MachineName`, `Environment.UserName`, `Environment.CurrentDirectory`, `Environment.Exit(` | Custom `IEnvironmentProvider` | | **Network** | `new HttpClient(`, `HttpClient.GetAsync(`, `HttpClient.PostAsync(`, `HttpClient.SendAsync(` | `IHttpClientFactory` (built-in) | | **Console** | `Console.WriteLine(`, `Console.ReadLine(`, `Console.Write(`, `Console.ReadKey(` | `IConsole` wrapper or `ILogger` | | **Process** | `Process.Start(`, `Process.GetCurrentProcess(`, `Process.GetProcessesByName(` | Custom `IProcessRunner` |

Step 3: Aggregate and rank results

Count each call site across the entire scan scope — including the instance-member call sites covered by the rules below, not only `static` ones.

**Counting rules — inaccurate totals are the main way this report loses to an ad-hoc scan:**

  • **One authoritative total.** Every call site you found belongs in the category summary and the grand total. Never park real findings in an "additional observations" section that the totals exclude.
  • **Classify by what the member touches, not by whether it is `static`.** Instance members that reach the same untestable resource still count and belong in the matching category (`new FileInfo(path).LastWriteTimeUtc` → File System; `httpClient.GetAsync(...)` → Network). Say "hidden dependency", not "static", when the member is an instance call.
  • **Exclude deterministic pure helpers from the "needs wrapping" total.** `Path.Combine`, `Path.GetExtension`, `Path.GetFileName`, and `Math.*`/`string.*` statics take no ambient input and are trivially testable. List them, if at all, in a separate "no action needed" note — never as testability blockers.
  • **Cover every cat
Read more
Ships withdotnet-skills

This repository contains the .NET team's curated set of core skills and custom agents for coding agents. For information about the Agent Skills standard, see agentskills.io. 📊 Dashboard - Accuracy and efficiency scoring trends for contained plugins (

Get the whole plugin

Other skills on dotnet-skills.