resharper-clt · v1.1.0 · 2026-08-24 · sha256 8692f43e461c09bd
resharper-clt v1.1.0A
Immutable. This exact content never changes and is served at /api/v1/blob/8692f43e461c09bd.
---
name: resharper-clt
description: Use when running JetBrains ReSharper Command Line Tools — InspectCode static analysis or CleanupCode formatting — over a .NET solution, to verify C# work before claiming it complete or to produce the analyzer gate output a code review triages. Do NOT use for non-.NET languages, for judging design or correctness by reading code, or as a substitute for the compiler and test gates.
license: MIT
metadata:
author: David R Palfery
version: 1.1.0
---
# ReSharper Command Line Tools
`InspectCode` runs ReSharper's inspection set over a solution and emits a machine-readable
report. `CleanupCode` applies ReSharper's formatting and cleanup profiles to source files.
Both are mechanical: the same solution at the same revision produces the same output, which
is what makes their result citable as evidence rather than an opinion.
That property is also the boundary. This skill produces and remediates tool output. It does
not judge design, correctness, or intent — those belong to the reviewer and its lenses.
## Setup
The tools ship as a .NET global tool, pinned in the repository's tool manifest at
`.config/dotnet-tools.json`:
```json
{
"version": 1,
"isRoot": true,
"tools": {
"jetbrains.resharper.globaltools": {
"version": "2026.2.1",
"commands": ["jb"],
"rollForward": false
}
}
}
```
ALWAYS restore before the first invocation in a session:
```bash
dotnet tool restore
```
If the repository has no manifest entry, say so and stop rather than installing a tool
version nobody pinned. A floating analyzer version makes every later report incomparable.
## Commands
Resolve the solution path from the repository rather than assuming one, and write reports to
the path declared as **<agent-scratchpad>** where the repository declares one. Substitute
`<solution>` and `<scratchpad>` below.
| Purpose | Command |
|---|---|
| Full-solution inspection | `dotnet jb inspectcode <solution> --output=<scratchpad>/inspectcode.xml --format=Xml` |
| Project-scoped inspection | `dotnet jb inspectcode <solution> --project="<ProjectName>" --output=<scratchpad>/inspect-<project>.xml --format=Xml` |
| SARIF for CI or automated scanning | `dotnet jb inspectcode <solution> --output=<scratchpad>/results.sarif --format=Sarif --severity=WARNING` |
| Formatting and cleanup | `dotnet jb cleanupcode <solution>` |
Scope the run to the affected projects when the solution is large. A full-solution pass is
correct for a completion gate and for a review of a broad change; a project-scoped pass is
enough while iterating.
## Running it before you claim work is complete
Any agent implementing, modifying, or refactoring C# MUST run this before reporting
`READY_FOR_REVIEW`.
The order matters, and step 2 is the point of the whole gate: **fix mechanically first, then
inspect what is left.** A defect a machine can fix is not a defect worth a reviewer's attention.
Every mechanical finding that reaches a reviewer costs a review pass to report, a rework cycle to
fix, and another pass to confirm — to arrive at the same edit `cleanupcode` would have made for
free.
1. **Baseline.** Before the first edit, inspect the projects you are permitted to change and
keep the report. Without it you cannot distinguish a diagnostic you introduced from one
that was already there, and "pre-existing" is a claim that needs proof.
2. **Fix deterministically, scoped to the files you changed.** Three commands, in this order,
each applying rather than verifying:
```bash
dotnet format <solution> --include <changed files>
dotnet format <solution> analyzers --include <changed files>
dotnet jb cleanupcode <solution> --profile="<cleanup profile>" --include="<changed files>"
```
`--include` is not optional. Unscoped, these rewrite files the task never touched, and the
change under review stops being the change you made. The pass is idempotent: running it twice
produces no second diff, so a re-run after a rework cycle is safe.
3. **Inspect again after the last edit**, over the same scope.
4. **Fix every ERROR and WARNING the change introduced that step 2 could not.** Compare against
the baseline; do not dismiss a finding merely because its line is untouched. What survives
step 2 is the genuinely non-mechanical remainder — a possible multiple enumeration, a
contradictory null contract — and that is the part worth your judgement.
5. **Report the result.** `dotnet build` and `dotnet jb inspectcode` both at zero errors and
zero code or logic warnings, or an explicit list of what remains with baseline proof.
### What the cleanup profile should and should not do
Fix what the repository's own standards and analyzers already require: predefined type keywords,
explicit types where the standard forbids `var`, redundant qualifiers and parentheses, unused
usings, fields that can be `readonly`, formatting. Those are settled decisions, and re-arguing one
in review is waste.
Leave everything the repository has **not** declared. Brace style with no `.editorconfig` rule
behind it, member reordering, and file layout are the tool's opinion rather than the project's
standard — and reordering members in particular buries the actual change under churn a reviewer
then has to read past. A cleanup profile that imposes undeclared preferences trades review cost
for diff noise, which is not the trade this gate exists to make.
A green build does not clear this. The compiler and ReSharper's inspection set overlap
partially and disagree at the edges by design — a suggestion-severity inspection is invisible
to `dotnet build` and still a real finding.
### Frequently introduced inspections, and what to do
| Inspection | Remediation |
|---|---|
| `PossibleMultipleEnumeration` | Materialize the `IEnumerable` with `.ToList()` or `.ToArray()` before iterating it more than once. |
| `ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract` | Fix the contradictory null check, or correct the nullable annotation on the DTO or API model it disagrees with. |
| `InheritdocInvalidUsage` | Replace `<inheritdoc />` on a type that inherits no documentation with a real `<summary>`. |
| `UseCollectionExpression` | Modernize the array or list initializer to a collection expression `[...]`. |
| `CheckNamespace` | Align the file's namespace to its directory hierarchy. |
| `UnusedParameter.Local`, `PrivateFieldCanBeConvertedToLocalVariable` | Remove the dead parameter, or narrow the single-assignment field to a local. |
| `RedundantSuppressNullableWarningExpression` | Remove the unnecessary `!`. Suppressing a warning the compiler no longer raises hides the next real one. |
**NEVER fix an inspection by breaking a public contract.** Deleting a public API action
method, an interface member, or a DTO serialization property to silence a "never used"
inspection trades a warning for a breaking change. Suppress it with a stated reason instead,
or leave it and say why.
## Running it as a review gate
In a code review this is a **gate**, not a lens: it is a repeatable command whose output the
`static-analysis-triage` lens attributes to the change. Declare it under `review.gates` in
`.kyber-weave/kyber-weave.yml` so the review runner executes it rather than the reviewer
improvising a command:
```yaml
review:
gates:
- id: inspectcode
run: [dotnet, jb, inspectcode, <solution>, --output=<scratchpad>/inspectcode.xml, --format=Xml]
blocking: true
```
`run` is argv, never a command line — the gate runner refuses a shell.
The gate result records the id, exit code, and duration — not the report. The
`static-analysis-triage` lens reads the report itself, from the `--output` path the gate
declaration names, so that path is part of the contract between them: change it in one place
and the lens finds nothing.
**InspectCode exits zero whether or not it finds anything.** `blocking: true` here means the
analyzer must have *run*, not that it found nothing. The judgement about what it found is the
lens's, and it is made against the diff — a solution-wide report is expected to carry
pre-existing findings, and attributing those to the change under review is the specific error
that lens exists to avoid.
The reviewer's own obligations around that output:
- **Do not re-derive it.** The lens attributes diagnostics to the diff and reports them by
rule identifier. Reading the code to form a second opinion about a rule is a different
lens's job.
- **Report by rule identifier and location.** "Analyzer warning" is not a finding.
- **A clean run is a recorded result, not silence.** State the gate ran and returned zero
findings.
- **An unavailable tool is a stated skip.** If the manifest has no entry or the restore
fails, record the gate as skipped with the reason. A review that quietly omits a gate reads
as a review that passed one.
## Worked example
A change adds a service that filters an `IEnumerable<Load>` and iterates the result twice.
`dotnet build` is green — nothing here is a compiler error.
```bash
dotnet tool restore
dotnet jb inspectcode Hauling.sln --project="Hauling.Application" --output=.scratch/inspect-after.xml --format=Xml
```
The report names one inspection the baseline did not have:
```xml
<Issue TypeId="PossibleMultipleEnumeration"
File="Hauling.Application/Dispatch/LoadPlanner.cs" Line="47"
Message="Possible multiple enumeration" />
```
Compare it against the baseline report to confirm the change introduced it, materialize the
sequence once with `.ToList()` before the first iteration, and re-run the same scoped
command. Then report it:
```text
DIAGNOSTICS: clean on Hauling.Application/Dispatch/LoadPlanner.cs | inspectcode: 0 errors / 0 warnings | baseline: .scratch/inspect-before.xml | remaining: none
```
Had the finding predated the change, the baseline report is the proof — cite it by path
rather than asserting "pre-existing".
## Boundaries
- .NET solutions only. There is no ReSharper CLT for other languages; say so rather than
substituting a different tool under this skill's name.
- `cleanupcode` rewrites source. It belongs to the completion gate above, run by the agent that
wrote the code, before any reviewer sees it — and NEVER as part of a review itself. A reviewer
that edits the code it is judging has stopped being a reviewer.
- Formatter output is not a review finding. Whitespace differences belong to `cleanupcode`, not
to the findings list. A reviewer that finds mechanical issues is looking at a change whose
completion gate did not run: the correct report is that single fact, not a list of nits the
gate would have erased.