From dotnet-10
Use when refactoring a C# symbol or concept and you need every usage caught reliably (typed C# + Razor `.cshtml` + string-typed name-variants), assembled into a structured knowledge graph the LLM navigates before deciding the refactor strategy. Closes the gaps that typed find-references misses by design — Razor string-typed helpers (`@Html.Partial("X")`, `@RenderSection`, `asp-action`), `ViewBag.X`, `ViewData["X"]`, HTML `name=` attributes, `[Bind(Include="...")]`, `[JsonProperty("X")]`, EF mapping strings, and case-variant drift (`PictureFileName` vs `pictureFileName` vs `picturefilename`). Applies to .NET / ASP.NET MVC 5 (legacy .NET Framework 4.x) and ASP.NET Core MVC code; the scripts have no project-load dependency, so they work on legacy and modern solutions alike. Do NOT use for refactor execution — Roslynator CLI, IDE refactorings, dotnet format, try-convert, and the GitHub Copilot modernization agent already cover that downstream.
How this skill is triggered — by the user, by Claude, or both
Slash command
/dotnet-10:code-usage-knowledge-graphThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
The skill produces `graph.json` for a concept (e.g. `CatalogItem.PictureFileName`)
The skill produces graph.json for a concept (e.g. CatalogItem.PictureFileName)
that an LLM consults before proposing a refactor. The graph captures every
reference site classified by kind, role (read/write/declaration), enclosing
context, and contract boundary status.
.cshtml, no string-typed): use LSP findReferences
through Microsoft.CodeAnalysis.LanguageServer or roslynator directly.rename-symbol), IDE refactorings,
try-convert, or the Copilot modernization agent.The skill ships four dotnet-script files in scripts/. Run them in this
order, or invoke assemble-graph.csx to run all three scanners and compose
in one shot.
dotnet script scripts/typed-cs-refs.csx -- \
--solution-dir <abs path to project subtree> \
--member <MemberName> \
--type <TypeName> \
--out /tmp/typed-cs-refs.json
Produces every IdentifierNameSyntax whose name matches --member, filtered
by type-affinity heuristics when --type is set (lambda parameters, foreach
loop variables, object initializers of the right type, bare identifiers
inside the type's own scope). No project load required — works on legacy
.NET Framework 4.x and modern .NET solutions.
.cshtml referencesdotnet script scripts/razor-refs.csx -- \
--solution-dir <abs path to project subtree> \
--symbol <MemberName> \
--out /tmp/razor-refs.json
Comprehensive Razor scan covering: @Html.<Helper>For lambdas (handles
modelItem => item.X foreach pattern), @Html.Partial/RenderPartial/Action/RenderAction,
@Url.Action/RouteUrl, @RenderSection, @Model.X raw access, @<id>.X
property access, ViewBag.X, ViewData["X"], ASP.NET Core tag helpers
(asp-for, asp-action, asp-controller), HTML <input name="X">, and
Razor directives (@model, @inherits, @inject, @using). Comments
(@*...*@) preserved with commented: true on the matching kind suffix.
See references/razor-discovery.md for the full kind taxonomy.
dotnet script scripts/string-typed-refs.csx -- \
--solution-dir <abs path to project subtree> \
--symbol <MemberName> \
--out /tmp/string-typed-refs.json
Roslyn-aware scan over .cs for [Bind], [JsonProperty], [Display],
nameof(), and other attribute / invocation contexts containing the
symbol name as a string literal. Plain text scan over .csv, .json,
.sql, .config, .xml with case-variant detection (PascalCase,
camelCase, lowercase, snake_case, SCREAMING_SNAKE, kebab-case).
.cshtml is intentionally excluded here — razor-refs.csx is the
authoritative .cshtml scanner with proper kind classification.
dotnet script scripts/assemble-graph.csx -- \
--solution-dir <abs path to project subtree> \
--type <TypeName> \
--member <MemberName> \
--typed-refs /tmp/typed-cs-refs.json \
--razor-refs /tmp/razor-refs.json \
--string-typed-refs /tmp/string-typed-refs.json \
--out /tmp/graph.json
Or run all three scanners and compose in one shot (subprocess call):
dotnet script scripts/assemble-graph.csx -- \
--solution-dir <abs path> \
--type CatalogItem --member PictureFileName \
--out /tmp/graph.json
See references/knowledge-graph-shape.md. Top-level fields:
concept — Type.Memberdeclaration — { file, line, col }nodes — every reference, classified by kind and roleedges — { from, to: nodeId, relation }summary — total_refs, by_kind, by_role, mutation_sites,
read_sites, contract_boundaries, drift_smellssources — counts from each scannerRead summary.contract_boundaries first — every entry is a place where a
refactor crosses a stable contract (MVC route, Bind attribute, JSON
serialization name, CSV header). Each boundary you cross either:
[Obsolete] alias, orRead summary.drift_smells next. Case-variant drift (multiple casings of
the same conceptual name) means a rename has to update each variant in its
own casing. The evidence[].variant and evidence[].variant_kind fields
tell you exactly which strings to update where.
Read summary.mutation_sites vs read_sites. A property with many
external mutation sites is an anemic-model smell — the refactor target may
be encapsulating mutation through a method on the type rather than renaming
the property.
Then design the refactor against the graph, citing node IDs as evidence. Hand the design to Roslynator CLI / IDE refactorings / the Copilot modernization agent for execution. Do NOT execute via this skill.
The scripts are heuristic-bounded — regex-based for Razor diversity,
syntactic (not semantic) for typed C#, pattern-list-based for string
contexts. They can miss patterns the codebase invents (custom
HtmlHelper extensions, source-generator output, Blazor .razor files,
SignalR hub method strings, route templates, custom attribute
serialization names) and they can over-match in unusual codebases.
Always cross-validate before trusting the graph. Run a baseline sanity check with the harness's standard tools and reconcile any discrepancy with the user before proceeding to refactor design.
# Case-insensitive identifier scan; the floor of what should be caught.
rg -i --word-regexp '<MemberName>' <solution-dir>
Compare the count to graph.summary.total_refs. Material discrepancy
(>5% miss, or a missing file the user expected) means the scripts under-
covered. Investigate before designing the refactor.
For symbol names that include compound casing variants
(PictureFileName / pictureFileName / picturefilename), also run:
rg --pcre2 '\b(PictureFileName|pictureFileName|picturefilename|picture_file_name|picture-file-name|PICTURE_FILE_NAME)\b' <solution-dir>
Every variant the script's summary.drift_smells.evidence flagged should
appear in this rg output. If rg surfaces a casing variant the script's
generator didn't include, extend the variant list in
string-typed-refs.csx.
When a scanner errors, returns 0 in a populated codebase, or under-covers relative to the rg cross-check:
| Scanner failure mode | Fallback path |
|---|---|
typed-cs-refs.csx crashes on parse, or returns 0 in a code-bearing project | (a) LSP findReferences at the declaration position via Microsoft.CodeAnalysis.LanguageServer if wired, (b) roslynator find-symbol --match "Name=='X'" <sln> if Roslynator can load the project, (c) rg --type cs '\b<MemberName>\b' <solution-dir> + manual Read of each hit's enclosing context, (d) dispatch the Explore Agent with a prompt to find every usage and classify by site type. |
razor-refs.csx misses a Razor pattern (custom helper, .razor Blazor file, source-generated cshtml) | (a) Extend the regex list in scripts/razor-refs.csx with the new pattern, (b) rg --type-add 'razor:*.{cshtml,razor}' --type razor '<MemberName>' <solution-dir> and manually classify, (c) for .razor Blazor specifically, the scanner does not handle it — use rg + Read directly. |
string-typed-refs.csx misses a string context the codebase uses (e.g. [Route("...")], [ProtoMember(Name="X")], custom attribute schemas) | (a) Extend ClassifyCsContext in scripts/string-typed-refs.csx with the new attribute name, (b) rg --pcre2 -i '<all-case-variants>' <solution-dir> filtered to the file types the codebase actually uses for that contract. |
assemble-graph.csx fails to spawn subprocess (dotnet-script not on PATH, sandbox restriction) | Run the three scanners individually with --out paths, then invoke assemble-graph.csx --typed-refs ... --razor-refs ... --string-typed-refs ... to compose pre-computed JSON. |
Solution truly unsupported (e.g. Roslyn parse errors on every .cs file due to preprocessor directives the script does not honor) | Skip the typed-cs scanner; rely on rg --word-regexp + Read + the Explore Agent. The skill degrades to a guided discovery rather than an automated one — the workflow shape is still useful even when the scripts cannot run. |
If after fallback the script-graph and the rg cross-check still disagree:
<file>:<line> (kind: …)". Let the user judge whether to extend the
scanner, hand-edit, or proceed with the smaller set.kind and
enclosing_context fields tell the LLM how to discount them.--type filter). Prefer LSP
findReferences when wired up correctly.Target: CatalogItem.PictureFileName in
dotnet-architecture/eShopModernizing/eShopLegacyMVCSolution/src/eShopLegacyMVC.
Ground truth: 35 references — 18 typed-cs, 12 razor, 5 string-typed/data.
Skill output:
[Bind] x2 + CSV header), 1 drift smell
(case-variant-drift across 3 casings).references/typed-tools.md — driving Microsoft.CodeAnalysis.LanguageServer
and Roslynator CLI for typed-cs queries (when LSP is correctly wired).references/razor-discovery.md — full kind taxonomy and pattern coverage
for razor-refs.csx.references/string-typed-discovery.md — case-variant generation,
controlled vocabulary of string-typed kinds.references/knowledge-graph-shape.md — JSON schema and example
traversals.Creates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.
npx claudepluginhub marafiq/dotnet-skills --plugin dotnet