/crap-score
Calculates targeted CRAP (Change Risk Anti-Patterns) scores for a named .NET method, class, or single source file. Use when the user explicitly asks to compute CRAP scores or assess risky untested code for a specific target, combining Cobertura coverage data with cyclomatic
$ npx -y skills add dotnet/skills --skill crap-score --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
/crap-score
Context preview
The summary Claude sees to decide when to auto-load this skill.
Calculates targeted CRAP (Change Risk Anti-Patterns) scores for a named .NET method, class, or single source file. Use when the user explicitly asks to compute CRAP scores or assess risky untested code for a specific target, combining Cobertura coverage data with cyclomatic
SKILL.md
crap-score.SKILL.mdname: crap-score
description: >
Calculates targeted CRAP (Change Risk Anti-Patterns) scores for a named .NET
method, class, or single source file. Use when the user explicitly asks to
compute CRAP scores or assess risky untested code for a specific target,
combining Cobertura coverage data with cyclomatic complexity analysis.
DO NOT USE FOR: project-wide coverage analysis, coverage plateau or "stuck
coverage" diagnosis, what's blocking coverage, or where to add tests across
a project (use coverage-analysis); writing tests; running tests without
CRAP context.
license: MIT
CRAP Score Analysis
Calculate CRAP (Change Risk Anti-Patterns) scores for .NET methods to identify code that is both complex and undertested.
Background
The CRAP score combines **cyclomatic complexity** and **code coverage** into a single metric:
$$\text{CRAP}(m) = \text{comp}(m)^2 \times (1 - \text{cov}(m))^3 + \text{comp}(m)$$
Where:
- $\text{comp}(m)$ = cyclomatic complexity of method $m$
- $\text{cov}(m)$ = code coverage ratio (0.0 to 1.0) of method $m$
| CRAP Score | Risk Level | Interpretation | |------------|------------|----------------| | < 5 | Low | Simple and well-tested | | 5-15 | Moderate | Acceptable for most code | | 15-30 | High | Needs more tests or simplification | | > 30 | Critical | Refactor and add coverage urgently |
A method with 100% coverage has CRAP = complexity (the minimum). A method with 0% coverage has CRAP = complexity^2 + complexity.
When to Use
- User wants to assess which methods are risky due to low coverage and high complexity
- User asks for CRAP score of specific methods, classes, or files
- User wants to prioritize which code to test next
- User wants to evaluate test quality beyond simple coverage percentages
When Not to Use
- User just wants to run tests (use `run-tests` skill)
- User wants to write new tests (use `writing-mstest-tests` skill or general coding assistance)
- User only wants a coverage percentage without complexity analysis
Inputs
| Input | Required | Description | |-------|----------|-------------| | Target scope | Yes | Method name, class name, or file path to analyze | | Test project path | No | Path to the test project. Defaults to discovering test projects in the solution. | | Source project path | No | Path to the source project under analysis |
Workflow
Step 1: Collect code coverage data
If no coverage data exists yet (no Cobertura XML available), **always run `dotnet test` with coverage collection first** and mention the exact command in your response. Do not skip this step -- CRAP scores require coverage data.
Check the test project's `.csproj` for the coverage package, then run the appropriate command:
| Coverage Package | Command | Output Location | |---|---|---| | `coverlet.collector` | `dotnet test --collect:"XPlat Code Coverage" --results-directory ./TestResults` | Typically under `TestResults/<guid>/coverage.cobertura.xml`. Search recursively under the results directory (for example, `TestResults/**/coverage.cobertura.xml`) or use any explicit coverage path the user provides. | | `Microsoft.Testing.Extensions.CodeCoverage` (.NET 9) | `dotnet test -- --coverage --coverage-output-format cobertura --coverage-output ./TestResults` | `--coverage-output` path | | `Microsoft.Testing.Extensions.CodeCoverage` (.NET 10+) | `dotnet test --coverage --coverage-output-format cobertura --coverage-output ./TestResults` | `--coverage-output` path |
Never estimate coverage
**Guessed coverage produces wrong CRAP scores, which is worse than no answer.** If the first command yields no Cobertura XML, work down this list before giving up:
1. Add a provider if none is referenced: `dotnet add <test.csproj> package coverlet.collector`, then re-run. 2. Use the standalone collector, which works even when the test host or a shared assembly blocks the in-proc collector: `dotnet tool install --global dotnet-coverage` then `dotnet-coverage collect -f cobertura -o coverage.cobertura.xml "dotnet test <test.csproj>"`. 3. Convert or summarize an existing report with ReportGenerator when only binary `.coverage` output exists: `dotnet tool install --global dotnet-reportgenerator-globaltool` then `reportgenerator -reports:<file> -targetdir:cov -reporttypes:Cobertura`. 4. Tests fail but still run? Coverage is collected from the tests that executed — continue with that data and note the failures.
If every path fails, **report that coverage could not be collected, show the commands you tried and their errors, and stop.** Report complexity on its own if useful, but never publish a CRAP number derived from an assumed coverage percentage.
Step 2: Compute cyclomatic complexity
Analyze the target source files to determine cyclomatic complexity per method. Count the following decision points (each adds 1 to the base complexity of 1):
| Construct | Example | |-----------|---------| | `if` | `if (x > 0)` | | `else if` | `else if (y < 0)` | | `case` (each) | `case 1:` | | `for` | `for (int i = 0; ...)` | | `foreach` | `foreach (var item in list)` | | `while` | `while (running)` | | `do...while` | `do { } while (cond)` | | `catch` (each) | `catch (Exception ex)` | | `&&` | `if (a && b)` | | `\|\|` (OR) | `if (a \|\| b)` | | `??` | `value ?? fallback` | | `?.` | `obj?.Method()` | | `? :` (ternary) | `x > 0 ? a : b` | | Pattern match arm | `x is > 0 and < 10` |
Base complexity is 1 for every method. Each decision point adds 1.
When analyzing, read the source file and count these constructs per method. Report the breakdown.
Step 3: Extract per-method coverage from Cobertura XML
Parse the Cobertura XML to find each method's `line-rate` attribute under the target `<class>` element. If `line-rate` is not available at method level, compute it from the `<lines>` elements:
$$\text{cov}(m) = \frac{\text{lines with hits} > 0}{\text{total lines}}$$
Method names in Cobertur
Read more
name: crap-score description: > Calculates targeted CRAP (Change Risk Anti-Patterns) scores for a named .NET method, class, or single source file. Use when the user explicitly asks to compute CRAP scores or assess risky untested code for a specific target, combining Cobertura coverage data with cyclomatic complexity analysis. DO NOT USE FOR: project-wide coverage analysis, coverage plateau or "stuck coverage" diagnosis, what's blocking coverage, or where to add tests across a project (use coverage-analysis); writing tests; running tests without CRAP context. license: MIT
CRAP Score Analysis
Calculate CRAP (Change Risk Anti-Patterns) scores for .NET methods to identify code that is both complex and undertested.
Background
The CRAP score combines **cyclomatic complexity** and **code coverage** into a single metric:
$$\text{CRAP}(m) = \text{comp}(m)^2 \times (1 - \text{cov}(m))^3 + \text{comp}(m)$$
Where:
- $\text{comp}(m)$ = cyclomatic complexity of method $m$
- $\text{cov}(m)$ = code coverage ratio (0.0 to 1.0) of method $m$
| CRAP Score | Risk Level | Interpretation | |------------|------------|----------------| | < 5 | Low | Simple and well-tested | | 5-15 | Moderate | Acceptable for most code | | 15-30 | High | Needs more tests or simplification | | > 30 | Critical | Refactor and add coverage urgently |
A method with 100% coverage has CRAP = complexity (the minimum). A method with 0% coverage has CRAP = complexity^2 + complexity.
When to Use
- User wants to assess which methods are risky due to low coverage and high complexity
- User asks for CRAP score of specific methods, classes, or files
- User wants to prioritize which code to test next
- User wants to evaluate test quality beyond simple coverage percentages
When Not to Use
- User just wants to run tests (use `run-tests` skill)
- User wants to write new tests (use `writing-mstest-tests` skill or general coding assistance)
- User only wants a coverage percentage without complexity analysis
Inputs
| Input | Required | Description | |-------|----------|-------------| | Target scope | Yes | Method name, class name, or file path to analyze | | Test project path | No | Path to the test project. Defaults to discovering test projects in the solution. | | Source project path | No | Path to the source project under analysis |
Workflow
Step 1: Collect code coverage data
If no coverage data exists yet (no Cobertura XML available), **always run `dotnet test` with coverage collection first** and mention the exact command in your response. Do not skip this step -- CRAP scores require coverage data.
Check the test project's `.csproj` for the coverage package, then run the appropriate command:
| Coverage Package | Command | Output Location | |---|---|---| | `coverlet.collector` | `dotnet test --collect:"XPlat Code Coverage" --results-directory ./TestResults` | Typically under `TestResults/<guid>/coverage.cobertura.xml`. Search recursively under the results directory (for example, `TestResults/**/coverage.cobertura.xml`) or use any explicit coverage path the user provides. | | `Microsoft.Testing.Extensions.CodeCoverage` (.NET 9) | `dotnet test -- --coverage --coverage-output-format cobertura --coverage-output ./TestResults` | `--coverage-output` path | | `Microsoft.Testing.Extensions.CodeCoverage` (.NET 10+) | `dotnet test --coverage --coverage-output-format cobertura --coverage-output ./TestResults` | `--coverage-output` path |
Never estimate coverage
**Guessed coverage produces wrong CRAP scores, which is worse than no answer.** If the first command yields no Cobertura XML, work down this list before giving up:
1. Add a provider if none is referenced: `dotnet add <test.csproj> package coverlet.collector`, then re-run. 2. Use the standalone collector, which works even when the test host or a shared assembly blocks the in-proc collector: `dotnet tool install --global dotnet-coverage` then `dotnet-coverage collect -f cobertura -o coverage.cobertura.xml "dotnet test <test.csproj>"`. 3. Convert or summarize an existing report with ReportGenerator when only binary `.coverage` output exists: `dotnet tool install --global dotnet-reportgenerator-globaltool` then `reportgenerator -reports:<file> -targetdir:cov -reporttypes:Cobertura`. 4. Tests fail but still run? Coverage is collected from the tests that executed — continue with that data and note the failures.
If every path fails, **report that coverage could not be collected, show the commands you tried and their errors, and stop.** Report complexity on its own if useful, but never publish a CRAP number derived from an assumed coverage percentage.
Step 2: Compute cyclomatic complexity
Analyze the target source files to determine cyclomatic complexity per method. Count the following decision points (each adds 1 to the base complexity of 1):
| Construct | Example | |-----------|---------| | `if` | `if (x > 0)` | | `else if` | `else if (y < 0)` | | `case` (each) | `case 1:` | | `for` | `for (int i = 0; ...)` | | `foreach` | `foreach (var item in list)` | | `while` | `while (running)` | | `do...while` | `do { } while (cond)` | | `catch` (each) | `catch (Exception ex)` | | `&&` | `if (a && b)` | | `\|\|` (OR) | `if (a \|\| b)` | | `??` | `value ?? fallback` | | `?.` | `obj?.Method()` | | `? :` (ternary) | `x > 0 ? a : b` | | Pattern match arm | `x is > 0 and < 10` |
Base complexity is 1 for every method. Each decision point adds 1.
When analyzing, read the source file and count these constructs per method. Report the breakdown.
Step 3: Extract per-method coverage from Cobertura XML
Parse the Cobertura XML to find each method's `line-rate` attribute under the target `<class>` element. If `line-rate` is not available at method level, compute it from the `<lines>` elements:
$$\text{cov}(m) = \frac{\text{lines with hits} > 0}{\text{total lines}}$$
Method names in Cobertur
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 (
Repo: dotnet/skills
Other skills on dotnet-skills.
- /csharp-scripts
Run file-based C# apps with the .NET CLI when the user explicitly wants C#/.NET code without creating a project. Use for C# language/API experiments, one-file C# apps, small multi-file C# apps composed with `#:include`/`#:exclude`, or C# file-based apps linked with `#:ref`. Do
Open skill - /dotnet-pinvoke
Correctly call native (C/C++) libraries from .NET using P/Invoke and LibraryImport. Covers function signatures, string marshalling, memory lifetime, SafeHandle, and cross-platform patterns. USE FOR: writing new P/Invoke or LibraryImport declarations, reviewing or debugging
Open skill - /nuget-trusted-publishing
Set up NuGet trusted publishing (OIDC) on a GitHub Actions repo — replaces long-lived API keys with short-lived tokens. USE FOR: trusted publishing, NuGet OIDC, keyless NuGet publish, migrate from NuGet API key, NuGet/login, secure NuGet publishing. DO NOT USE FOR: publishing to
Open skill - /technology-selection
Guides technology selection and implementation of AI and ML features in .NET 8+ applications using ML.NET, Microsoft.Extensions.AI (MEAI), Microsoft Agent Framework (MAF), GitHub Copilot SDK, ONNX Runtime, and OllamaSharp. Covers the full spectrum from classic ML through modern
Open skill - /configuring-opentelemetry-dotnet
Configure OpenTelemetry distributed tracing, metrics, and logging in ASP.NET Core using the .NET OpenTelemetry SDK. Use when adding observability, setting up OTLP exporters, creating custom metrics/spans, or troubleshooting distributed trace correlation.
Open skill - /convert-blazor-server-to-webapp
Guides conversion of a pre-.NET 8 Blazor Server app into a .NET 8+ Blazor Web App. USE FOR: migrating apps that use AddServerSideBlazor and MapBlazorHub to the AddRazorComponents/MapRazorComponents model, converting _Host.cshtml to an App.razor root component, replacing
Open skill

