roslyn-discovery
Skill2 starsChanged 43 days agoMITView on GitHub
Use to answer structural questions about a C#/.NET codebase that grep and LSP answer badly — public API surface, who implements an interface or derives from a base, what's undocumented, call/usage inventories. Write a throwaway read-only Roslyn program (a single-file `dotnet run query.cs` app) that parses the real syntax/semantic graph instead of text-matching. Roslyn knows the code; grep guesses.
In this repository56 other files it gives its agents.
- AGENTS.mdAGENTS.md
- CLAUDE.mdCLAUDE.md
- benchmarkingSkill
- commit-messagesSkill
- csharp-15-syntaxSkill
- csharp-aot-trimmingSkill
- csharp-api-designSkill
- csharp-assembly-inspectionSkill
- csharp-asyncSkill
- csharp-collections-modernSkill
- csharp-concurrency-primitivesSkill
- csharp-data-layoutSkill
- csharp-debuggingSkill
- csharp-discardsSkill
- csharp-disposable-patternsSkill
- csharp-docsSkill
- csharp-error-handlingSkill
- csharp-extension-membersSkill
- csharp-generics-modernSkill
- csharp-guard-clausesSkill
- csharp-json-source-generationSkill
- csharp-language-versionsSkill
- csharp-logging-diagnosticsSkill
- csharp-lspSkill
- csharp-modern-typesSkill
- csharp-nullabilitySkill
- csharp-nullable-migrationSkill
- csharp-nunitSkill
- csharp-pattern-matchingSkill
- csharp-performanceSkill
- csharp-polyfillsSkill
- csharp-source-generatorsSkill
- csharp-static-lambdasSkill
- csharp-string-handlingSkill
- csharp-tunitSkill
- csharp-verificationSkill
- dispatching-subagentsSkill
- dotnet-11-librariesSkill
- dotnet-analyzersSkill
- dotnet-central-package-managementSkill
- dotnet-file-based-appsSkill
- dotnet-msbuild-usingsSkill
- dotnet-multi-targetingSkill
- dotnet-project-hygieneSkill
- dotnet-runSkill
- dotnet-runtime-asyncSkill
- dotnet-slnxSkill
- dotnet-test-platformsSkill
- executing-plansSkill
- performance-bounded-deduplicationSkill
- roslyn-analyzer-performanceSkill
- roslyn-duplicate-detectionSkill
- roslyn-guard-rewritersSkill
- roslyn-rewritersSkill
- subagent-driven-developmentSkill
- tddSkill
The file
We split this skill into the rules it gives an agent
3 rules. About 0 are common, 1 is distinctive, and 2 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈1
Distinctive · estimate
This author's own choices. Worth a look.
2
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 12
If the question is about the compiled output rather than the source tree, stop and switch skills: manifest resources, assembly references, …
- Very specific: line 21
Default to syntax-only; step up only when the question genuinely needs binding.
- Distinctive: line 40
Report what you skipped: If you sampled, capped, or ignored a project, say so — a silent omission reads as "nothing there."
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Roslyn discovery — querying a codebase, reliably
Some questions about a codebase can't be answered by text search: which types implement IHandler (including transitively)? what's our public API surface? which public members lack docs? where is this symbol actually used, as opposed to where its name appears in a comment? Grep hits strings and comments and misses indirect relationships; LSP find-references serves a stale, sometimes-incomplete index (see csharp-lsp). A small read-only Roslyn program parses the actual syntax tree — or resolves the real semantic type graph — and gives you a complete, correct answer.
This is the read-only sibling of roslyn-rewriters: same single-file dotnet run harness, same "parse, don't guess" principle, but it reports instead of editing. A common pairing is discover here → feed the work-list to a rewriter: find every IHandler, then drive an edit across exactly those files.
If the question is about the compiled output rather than the source tree, stop and switch skills: manifest resources, assembly references, shipped members, package payloads, and WinUI/PRI questions belong to csharp-assembly-inspection, not Roslyn.
Fighting the shell is the signal to switch. The moment you're escaping quotes, threading a file list through xargs, or debugging why a loop printed nothing, stop — that friction means the question is structural, and a Roslyn query will be shorter and correct. Shell quirks make it worse than it looks: fish does not word-split a space-joined string (only arrays/newlines split), so a $files variable arrives as one giant argument; rg -h is --help, not "no headers" (use --no-filename); \K needs -P. Don't burn three attempts wrestling these — the second failed grep/rg pipeline is the cue to write query.cs.
Two modes, same as rewriting
- Syntax-only (parse text, no build) — enough for anything you can see in the tree: API surface, attribute usage, missing doc comments,
async void, method/type inventories. Fast, no project context, justCSharpSyntaxTree.ParseText. - Semantic (load the solution via
MSBuildWorkspace) — needed the moment a question depends on what a name binds to: implementations of an interface, derived types, real call sites, overload resolution, "is thisourCacheor the BCL one." You get anINamedTypeSymbol/SemanticModelgraph that knows the truth across projects and referenced assemblies.
Default to syntax-only; step up only when the question genuinely needs binding. (Same MSBuildLocator.RegisterDefaults() rule and latest-stable Roslyn guidance as roslyn-rewriters — pin all Microsoft.CodeAnalysis.* to the same current 5.0.0-line version; the newer line keeps the MSBuild workspace restore advisory-clean.)
Runnable examples
./examples/ has two complete single-file apps:
01-api-surface.cs— syntax-only. Enumerates every public/protected type and member and flags the undocumented ones (--undocumented-onlyto list just those). For API review, catching accidentalpublic, and finding doc gaps before a release.02-find-implementations.cs— semantic. Across a whole solution, lists every type that implements an interface or derives from a base by name, withfile:lineand whether it'simplementsvsderives. Catches transitive/indirect relationships grep can't, and builds a reliable work-list.
Copy one and adapt the query. They're disposable — don't commit them into the product repo unless the team wants to keep a query around.
Writing a good discovery query
- Walk the right nodes.
root.DescendantNodes().OfType<MethodDeclarationSyntax>()etc. for syntax; recurseINamespaceSymbol/INamedTypeSymbol(as02does) for semantic.SymbolFinder.FindImplementationsAsync/FindDerivedClassesAsync/FindReferencesAsyncare the idiomatic semantic shortcuts when you already hold the target symbol. - Reference scans: compare identifier-token sets, not regexes. To ask "does this file use type
Foo," collectDescendantTokens().Where(t => t.IsKind(SyntaxKind.IdentifierToken)).Select(t => t.ValueText)into aHashSetand test membership. This ignores comments and string literals for free — the thing regex gets wrong — and stays syntax-only (fast, no build). Collect declared names fromBaseTypeDeclarationSyntax/DelegateDeclarationSyntaxidentifiers. - Partition + dependency closure (fixpoint). A common split decision — "which files are type-agnostic enough for the
.Coreproject vs. must stay behind the shim?" — is a discovery query: seed the "tainted" set with files referencing the forbidden types, then iterate: any file that references a type declared only in the tainted set joins it, repeat until no change. The fixpoint catches transitive entanglement a one-pass scan misses, and proves the partition is closed (printing the count converged-to and which files were promoted, and why). Don't eyeball transitivity — loop it. - Skip
obj//bin/so you don't survey generated or build output. - Match visibility from the right default. Top-level types are
internalby default, members areprivate— only explicitpublic/protectedis API surface (seeIsVisiblein01). - Emit
file:linefor anything actionable (location.GetLineSpan()), and print a count summary at the end — discovery output is something you act on. - Report what you skipped. If you sampled, capped, or ignored a project, say so — a silent omission reads as "nothing there."
- It's read-only. A discovery tool should never write source files. If you find yourself wanting to edit, that's
roslyn-rewriters.
When NOT to reach for this
- A genuinely simple, literal-text question (find a constant string) — just use a text search.
- A one-off "where's this method" navigation — LSP go-to-definition is fine for that (and
csharp-lspcovers when to trust it). - Reach for Roslyn when the question is structural or semantic and a wrong/incomplete answer would matter — completeness-critical sweeps, refactor work-lists, API audits.
Anti-patterns
- Grepping for an interface name to find implementers — misses indirect implementers and base-class chains, hits comments/strings.
- Trusting LSP find-references as a complete list for a risky sweep — cross-check with a semantic query.
- Loading a full
MSBuildWorkspacefor a question syntax could answer — slower, more setup, no benefit. - A "discovery" tool that mutates files — keep read and write separate; rewriting is
roslyn-rewriters. - Reporting a partial sweep as if it were complete — print the count and anything skipped.
- Wrestling shell quoting /
fishword-splitting /xargsto do structural analysis — the friction is the signal; write the query in Roslyn instead of fighting the shell a third time. - Regex over raw file text for "uses type X" — matches comments and strings; compare identifier-token sets instead.
- Eyeballing a one-pass scan for a split/partition decision and assuming it's closed — iterate to a fixpoint so transitive dependencies can't leak across the boundary.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

