/find-untested-sources
MANDATORY for static requests to find, identify, or list untested source files or modules, sources without tests, source-to-test pairing, test-gap worklists, or suggested test locations. Invoke even for a tiny package; do not substitute manual globbing. Uses Roslyn for C#/.NET
$ npx -y skills add managedcode/dotnet-skills --skill find-untested-sources --agent claude-codeHow 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
/find-untested-sources
Context preview
The summary Claude sees to decide when to auto-load this skill.
MANDATORY for static requests to find, identify, or list untested source files or modules, sources without tests, source-to-test pairing, test-gap worklists, or suggested test locations. Invoke even for a tiny package; do not substitute manual globbing. Uses Roslyn for C#/.NET
SKILL.md
find-untested-sources.SKILL.mdname: find-untested-sources
description: >
MANDATORY for static requests to find, identify, or list untested source files
or modules, sources without tests, source-to-test pairing, test-gap worklists,
or suggested test locations. Invoke even for a tiny package; do not substitute
manual globbing. Uses Roslyn for C#/.NET and tree-sitter for Python, TS/JS, Go,
Java, Rust, and Ruby. DO NOT USE FOR: line/branch coverage, CRAP risk, or
grading existing tests.
license: MIT
Find Untested Sources
Purpose
Coverage tools answer "which lines were executed?" — they require a green build and a passing test run, which is minutes-to-tens-of-minutes on a real repo. The question this skill answers is different and much cheaper:
> _Which source files have no test file referencing any of their declared > types/symbols?_
That's the question an agent asks **before** writing a new test — and it can be answered statically in a few seconds by parsing source files, with **no build, no dependency resolution, and no compilation**. The output is a deterministic test-pairing map that lets the agent pick the next file to test without reading the entire codebase first.
Two engines — pick one
This skill ships two interchangeable analyzers with a compatible JSON contract:
| Engine | Script | Use when | |--------|--------|----------| | **Roslyn (C#)** | `scripts/Find-UntestedSources.cs` | The repo is **.NET-only**. Parses every `.cs` file with the Roslyn syntax API and does strict **namespace disambiguation**, so it is materially more accurate on duplicated short names like `Settings` or `Context`. | | **tree-sitter (polyglot)** | `scripts/find_untested_sources.py` | The repo is **not exclusively C#**, or you want one tool across Python, TypeScript/JavaScript, Go, Java, Rust, Ruby, and C#. |
For a .NET-only repository, **prefer the Roslyn engine** — its namespace-aware pairing beats the polyglot engine's identifier overlap.
Required workflow
1. Use the narrowest repository or package root named by the caller. Do not scan a parent workspace when the request identifies a subdirectory. 2. Execute the appropriate analyzer once. Do not replace analyzer execution with manual globbing, filename matching, or visual inspection. For polyglot analysis, pass `--include-tested` when the answer must distinguish paired sources from unpaired sources. 3. Base the result on the analyzer's JSON. Preserve its paired/unpaired classification and suggested relative path; do not guess a different path. 4. When the caller named a subdirectory, prefix analyzer-relative paths with that subdirectory so reported paths are workspace-relative. 5. Report the requested result plus the static-pairing coverage caveat. Do not append build, package-install, test-run, or coverage commands. When paired sources exist, name their covering test files so the unpaired classification is auditable.
When to Use
- User asks "where should I add tests?", "which files have no tests?", "find
untested code", "give me a test gap list", "what's the next file to test".
- Before invoking a test-generation agent, to produce a prioritized worklist.
- After generating tests, to verify each new test file pairs to a source file.
- To enumerate "weakly paired" source files (only one referring test) for
follow-up depth checks.
When Not to Use
- **Line/branch coverage** — use `coverage-analysis`.
- **CRAP-score / risk hotspots** — use `coverage-analysis`.
- **Are existing tests strong?** — use `test-gap-analysis` (mutation reasoning)
or `assertion-quality`.
Roslyn engine (C#)
Prerequisites
- .NET SDK that supports file-based apps (`dotnet run script.cs`). Pinned in the
repo's `global.json` (SDK 11 preview or later).
- No internet access required beyond the initial NuGet restore of
`Microsoft.CodeAnalysis.CSharp` on first run.
Usage
# From the skill folder
dotnet run scripts/Find-UntestedSources.cs -- <repo-root> [--top N]
# Save the report
dotnet run scripts/Find-UntestedSources.cs -- <repo-root> > pairing.json
# Iterate the untested list, highest-API-surface first
$report = Get-Content pairing.json | ConvertFrom-Json
$report.untested | Select-Object -First 10 source, decl_count, suggested_test_path
Diagnostics go to stderr; JSON goes to stdout.
Output schema
{
"repo": "<absolute path>",
"elapsed_ms": 8883,
"counts": {
"source_files": 3036,
"test_files": 867,
"untested_files": 1852,
"paired_files": 1184
},
"untested": [
{
"source": "src/Foo/Bar.cs",
"decl_count": 8, // # of type declarations in the file
"suggested_test_path": // mirror of source under a discovered test project
"tests/Foo.Tests/Bar/BarTests.cs"
}
],
"source_to_tests": {
"src/Foo/Baz.cs": [
"tests/Foo.Tests/BazTests.cs",
"tests/Foo.IntegrationTests/Scenarios/BazScenarios.cs"
]
}
}How it works
1. **File discovery** — recursive walk pruning `bin/`, `obj/`, `node_modules/`, `.git/`, `.vs/`, `packages/`, and any dotted subdir. Skips generated files (`.g.cs`, `.Designer.cs`, `.AssemblyInfo.cs`). 2. **Test vs source classification** — walks up to the nearest `.csproj` and marks it a test project if the project name ends in `.Tests`, `.Test`, `.UnitTests`, `.IntegrationTests`, `.E2E`, `.EndToEnd`, `.Spec`, `.Specs`, or the content references `Microsoft.NET.Test.Sdk`, `MSTest.Sdk`, `Microsoft.Testing.Platform`, `xunit`, `NUnit`, `TUnit`, or `<IsTestProject>true</IsTestProject>`. 3. **Source index (parallel)** — parse each source file with `CSharpSyntaxTree.ParseText` (syntax only, no compilation); record every `BaseTypeDeclarationSyntax` / `DelegateDeclarationSyntax` as `(ShortName, EnclosingNamespace, FilePath)`. 4. **Test scan (parallel)** — parse each test file, collect `using` directives + enclosing namespace, walk every `IdentifierToken`, loo
Read more
name: find-untested-sources description: > MANDATORY for static requests to find, identify, or list untested source files or modules, sources without tests, source-to-test pairing, test-gap worklists, or suggested test locations. Invoke even for a tiny package; do not substitute manual globbing. Uses Roslyn for C#/.NET and tree-sitter for Python, TS/JS, Go, Java, Rust, and Ruby. DO NOT USE FOR: line/branch coverage, CRAP risk, or grading existing tests. license: MIT
Find Untested Sources
Purpose
Coverage tools answer "which lines were executed?" — they require a green build and a passing test run, which is minutes-to-tens-of-minutes on a real repo. The question this skill answers is different and much cheaper:
> _Which source files have no test file referencing any of their declared > types/symbols?_
That's the question an agent asks **before** writing a new test — and it can be answered statically in a few seconds by parsing source files, with **no build, no dependency resolution, and no compilation**. The output is a deterministic test-pairing map that lets the agent pick the next file to test without reading the entire codebase first.
Two engines — pick one
This skill ships two interchangeable analyzers with a compatible JSON contract:
| Engine | Script | Use when | |--------|--------|----------| | **Roslyn (C#)** | `scripts/Find-UntestedSources.cs` | The repo is **.NET-only**. Parses every `.cs` file with the Roslyn syntax API and does strict **namespace disambiguation**, so it is materially more accurate on duplicated short names like `Settings` or `Context`. | | **tree-sitter (polyglot)** | `scripts/find_untested_sources.py` | The repo is **not exclusively C#**, or you want one tool across Python, TypeScript/JavaScript, Go, Java, Rust, Ruby, and C#. |
For a .NET-only repository, **prefer the Roslyn engine** — its namespace-aware pairing beats the polyglot engine's identifier overlap.
Required workflow
1. Use the narrowest repository or package root named by the caller. Do not scan a parent workspace when the request identifies a subdirectory. 2. Execute the appropriate analyzer once. Do not replace analyzer execution with manual globbing, filename matching, or visual inspection. For polyglot analysis, pass `--include-tested` when the answer must distinguish paired sources from unpaired sources. 3. Base the result on the analyzer's JSON. Preserve its paired/unpaired classification and suggested relative path; do not guess a different path. 4. When the caller named a subdirectory, prefix analyzer-relative paths with that subdirectory so reported paths are workspace-relative. 5. Report the requested result plus the static-pairing coverage caveat. Do not append build, package-install, test-run, or coverage commands. When paired sources exist, name their covering test files so the unpaired classification is auditable.
When to Use
- User asks "where should I add tests?", "which files have no tests?", "find
untested code", "give me a test gap list", "what's the next file to test".
- Before invoking a test-generation agent, to produce a prioritized worklist.
- After generating tests, to verify each new test file pairs to a source file.
- To enumerate "weakly paired" source files (only one referring test) for
follow-up depth checks.
When Not to Use
- **Line/branch coverage** — use `coverage-analysis`.
- **CRAP-score / risk hotspots** — use `coverage-analysis`.
- **Are existing tests strong?** — use `test-gap-analysis` (mutation reasoning)
or `assertion-quality`.
Roslyn engine (C#)
Prerequisites
- .NET SDK that supports file-based apps (`dotnet run script.cs`). Pinned in the
repo's `global.json` (SDK 11 preview or later).
- No internet access required beyond the initial NuGet restore of
`Microsoft.CodeAnalysis.CSharp` on first run.
Usage
# From the skill folder dotnet run scripts/Find-UntestedSources.cs -- <repo-root> [--top N] # Save the report dotnet run scripts/Find-UntestedSources.cs -- <repo-root> > pairing.json # Iterate the untested list, highest-API-surface first $report = Get-Content pairing.json | ConvertFrom-Json $report.untested | Select-Object -First 10 source, decl_count, suggested_test_path
Diagnostics go to stderr; JSON goes to stdout.
Output schema
{
"repo": "<absolute path>",
"elapsed_ms": 8883,
"counts": {
"source_files": 3036,
"test_files": 867,
"untested_files": 1852,
"paired_files": 1184
},
"untested": [
{
"source": "src/Foo/Bar.cs",
"decl_count": 8, // # of type declarations in the file
"suggested_test_path": // mirror of source under a discovered test project
"tests/Foo.Tests/Bar/BarTests.cs"
}
],
"source_to_tests": {
"src/Foo/Baz.cs": [
"tests/Foo.Tests/BazTests.cs",
"tests/Foo.IntegrationTests/Scenarios/BazScenarios.cs"
]
}
}How it works
1. **File discovery** — recursive walk pruning `bin/`, `obj/`, `node_modules/`, `.git/`, `.vs/`, `packages/`, and any dotted subdir. Skips generated files (`.g.cs`, `.Designer.cs`, `.AssemblyInfo.cs`). 2. **Test vs source classification** — walks up to the nearest `.csproj` and marks it a test project if the project name ends in `.Tests`, `.Test`, `.UnitTests`, `.IntegrationTests`, `.E2E`, `.EndToEnd`, `.Spec`, `.Specs`, or the content references `Microsoft.NET.Test.Sdk`, `MSTest.Sdk`, `Microsoft.Testing.Platform`, `xunit`, `NUnit`, `TUnit`, or `<IsTestProject>true</IsTestProject>`. 3. **Source index (parallel)** — parse each source file with `CSharpSyntaxTree.ParseText` (syntax only, no compilation); record every `BaseTypeDeclarationSyntax` / `DelegateDeclarationSyntax` as `(ShortName, EnclosingNamespace, FilePath)`. 4. **Test scan (parallel)** — parse each test file, collect `using` directives + enclosing namespace, walk every `IdentifierToken`, loo
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.
Repo: managedcode/dotnet-skills
Other skills on dotnet-skills.
- /aspnet-core
Build, debug, modernize, or review ASP.NET Core applications with correct hosting, middleware, security, configuration, logging, and deployment patterns on current .NET. USE FOR: working on ASP.NET Core apps, services, or middleware; changing auth, routing, configuration,
Open skill - /aspire
Build, upgrade, and operate Aspire 13.4.x C# or TypeScript application hosts with the current CLI, AppHost, ServiceDefaults, integrations, dashboard, testing, MCP, and deployment patterns for distributed apps. USE FOR: Aspire.AppHost.Sdk, Aspire.Hosting.*,
Open skill - /azure-functions
Build, review, or migrate Azure Functions in .NET with correct execution model, isolated worker setup, bindings, DI, and Durable Functions patterns. USE FOR: working on Azure Functions in .NET; migrating from the in-process model to the isolated worker model; adding Durable
Open skill - /blazor
Build and review Blazor applications across server, WebAssembly, web app, and hybrid scenarios with correct component design, state flow, rendering, and hosting choices. USE FOR: building interactive web UIs with C# instead of JavaScript; choosing between Server, WebAssembly, or
Open skill - /entity-framework6
Maintain or migrate EF6-based applications with realistic guidance on what to keep, what to modernize, and when EF Core is or is not the right next step. USE FOR: EF6 codebases; runtime versus ORM migration decisions; EDMX, code-first, ObjectContext, and legacy data-access
Open skill - /entity-framework-core
Design, tune, or review EF Core data access with proper modeling, migrations, query translation, performance, and lifetime management for modern .NET applications. USE FOR: DbContext, migrations, model configuration, EF queries, tracking, loading, performance, transactions, and
Open skill

