| name | iru-dotnet-code-quality |
| description | Run this .NET/C# project or solution's Roslyn analyzers — StyleCop.Analyzers for style/formatting (the .NET equivalent of Checkstyle) and Microsoft.CodeAnalysis.NetAnalyzers' CA rules for code-quality/design/reliability/security bug detection (the .NET equivalent of PMD + SpotBugs combined) — via a clean `dotnet build` with a SARIF diagnostic log, then read the generated `.sarif` file to summarize every issue found (file, line, rule, message), grouped by tool. Invoke as `/iru-dotnet-code-quality` to check the whole solution, or `/iru-dotnet-code-quality <path-or-class-name>` to scope the summary to a specific project/file/class — the underlying build always analyzes the whole targeted solution/project (Roslyn analyzers have no per-class selector), only the reported summary is filtered. Use whenever the user wants a lint/static-analysis pass called out separately from `iru-dotnet-test`/`iru-dotnet-coverage`, instead of eyeballing raw build warnings. |
| model | haiku |
.NET Quality
Run StyleCop.Analyzers and the CA (Code Analysis) rules from Microsoft.CodeAnalysis.NetAnalyzers directly through a
build, and report every issue they find. Both are Roslyn diagnostic analyzers, so both surface through the same
build pipeline and the same SARIF log — there's no separate per-tool CLI invocation the way Checkstyle/PMD/
SpotBugs each have their own Maven goal. This skill only runs static analysis and reports results — it does not
fix issues unless asked to as a follow-up.
Step 1 — Confirm the analyzers are actually wired up
Before running anything, check what's actually configured, since a clean report from a tool that isn't enabled
would be misleading:
- StyleCop.Analyzers: look for a
<PackageReference Include="StyleCop.Analyzers" ... /> in the target
project's .csproj or a shared Directory.Build.props/Directory.Packages.props. Note whether it's configured
via stylecop.json (referenced as an <AdditionalFiles> item) or via .editorconfig
dotnet_diagnostic.SA####.severity entries — either is valid.
- CA rules (Microsoft.CodeAnalysis.NetAnalyzers): for SDK-style projects targeting .NET 5+, these ship
built into the SDK and are enabled by default (
EnableNETAnalyzers defaults to true); check
<AnalysisLevel>/<AnalysisMode> in the .csproj (e.g. AnalysisMode=All enables every CA rule, not just
the Recommended default subset) and .editorconfig dotnet_diagnostic.CA####.severity overrides. For
projects targeting older frameworks, or that want a pinned analyzer version, check instead for an explicit
<PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" ... />.
- If neither is configured on the project(s) in scope, say so plainly in the Step 4 report instead of reporting a
false "zero issues" — the build will simply have nothing to emit for that tool.
Step 2 — Run a clean build with a SARIF diagnostic log
dotnet clean [<path-to-.sln-or-.csproj>]
dotnet build [<path-to-.sln-or-.csproj>] --no-incremental "-p:ErrorLog=./dotnet-code-quality.sarif%2Cversion=2"
dotnet clean first, then --no-incremental, is required for the same reason mvn clean compile precedes
SpotBugs: an up-to-date incremental build can skip re-analyzing unchanged files, silently leaving a stale (or
absent) SARIF log rather than one reflecting the current source.
-p:ErrorLog=<path>,version=2 tells the Roslyn compiler to emit every diagnostic — compiler and analyzer alike —
as a SARIF v2.1 log at <path>, regardless of whether the build itself succeeds or fails. The %2C escapes the
comma so MSBuild doesn't split it into two properties; quote the whole -p: argument as shown.
- If the working directory (or repository root) contains a single
.sln, dotnet build/dotnet clean target it
by default — no need to name it explicitly, same convention as iru-dotnet-test. If there are multiple .sln files
and no obvious default, ask which one to target unless the user already named a project/path.
- If the build fails outright due to a genuine compile error (not just an analyzer warning promoted by
TreatWarningsAsErrors/WarningsAsErrors), the SARIF log may be incomplete or absent for the affected
project(s) — report the compile failure and don't read a stale prior log. If the failure is purely
warnings-as-errors from StyleCop/CA rules, the SARIF log is still written (diagnostics are captured as they're
emitted, before that promotion fails the build) — proceed to read it normally.
Step 3 — Read the generated SARIF log, scoped to the request if given
./dotnet-code-quality.sarif (or wherever ErrorLog pointed) can be large on a solution with many projects/
issues, and the build in Step 2 still analyzed the whole targeted solution/project regardless of scope (Roslyn
analyzers have no per-file CLI selector equivalent to Surefire's -Dtest) — so a scoped invocation should filter
before reading the full file rather than after.
With no argument (whole solution): read the file in full.
With a specific project/file/class name: if jq is available (jq --version), filter to just the matching
results instead of loading the whole file into context:
jq '[.runs[].results[] | select(.locations[0].physicalLocation.artifactLocation.uri | endswith("/<Name>.cs"))]' \
./dotnet-code-quality.sarif
(match the named project's directory instead of a .cs suffix if scoping to a whole project). If jq isn't
available, read the full file — there's no clean line-based extraction for JSON the way awk's range pattern
works for the Checkstyle/PMD skill's XML, so filtering after a full read is the fallback here, not a first
resort.
Its structure groups results by run/tool:
{
"runs": [
{
"tool": { "driver": { "name": "Microsoft.CodeAnalysis.CSharp", "rules": [ ... ] } },
"results": [
{
"ruleId": "SA1101",
"level": "warning",
"message": { "text": "Prefix local calls with this" },
"locations": [
{
"physicalLocation": {
"artifactLocation": { "uri": "Services/Foo.cs" }
Every project built contributes its own run entry, but results across all runs use the same ruleId scheme —
classify by the ruleId prefix, not by which run/tool.driver.name it came from:
SA (and SX) prefix → StyleCop.Analyzers — style/formatting/ordering issues, the equivalent of Checkstyle
findings.
CA prefix → Microsoft.CodeAnalysis.NetAnalyzers — design, reliability, performance, and security rules,
the equivalent of PMD + SpotBugs findings combined into one rule family.
CS prefix → plain compiler warnings/errors (nullable-reference warnings, obsolete-API warnings, etc.) —
not from either analyzer; note them separately if present, but they're out of this skill's primary scope.
IDEnnnn prefix → built-in .NET "IDE style" analyzers driven by .editorconfig dotnet_style_*/
csharp_style_* settings — also not from either named tool; note separately if present, same as CS.
For each result, record: file (artifactLocation.uri, relative to the project), line (region.startLine), rule
ID, severity (level: error/warning/note), and the message text. A ruleId not present in tool.driver. rules for that run can still be looked up in another run's rules array if the same analyzer package is
referenced by multiple projects.
Step 4 — Report
Group findings by tool (StyleCop.Analyzers, then CA rules), then by file. For each issue, give file:line, the rule
ID, severity, and the message. State a total count per tool at the top and an overall total; if a tool found zero
issues in the requested scope, say so — but if that's because the tool isn't actually configured (Step 1), say
that explicitly instead of implying a clean pass. If CS/IDE diagnostics were also present, mention them briefly
as a separate, out-of-scope bucket rather than folding them into either tool's count.
Do not attempt to fix any of the issues found, or modify project files/.editorconfig/stylecop.json — that's a
follow-up the user drives explicitly.