This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
- Windows-only WPF application (
CSharpCodeAnalyst.csprojtargetsnet10.0-windows,OutputType=WinExe). Building / running on Linux/macOS is not supported. - Requires .NET 10 SDK (build) and the .NET 10 Runtime plus MSBuild from a Visual Studio or .NET SDK install (the parser loads solutions through
MSBuildWorkspace). - Line endings: the repo is full of Windows-native WPF assets; do not reformat or convert CRLFs.
Run from the repository root.
# Restore and build the whole solution
dotnet restore
dotnet build
# Release build with explicit version (matches CI)
dotnet build --no-restore -c Release -p:Version=0.9.0 -p:FileVersion=0.9.0 -p:AssemblyVersion=0.9.0
# Run all tests
dotnet test --configuration Release
# Run a single NUnit fixture or test (filter by fully-qualified name)
dotnet test Tests/Tests.csproj --filter "FullyQualifiedName~CyclesApprovalTests"
dotnet test Tests/Tests.csproj --filter "FullyQualifiedName=CodeParserTests.ApprovalTests.CyclesApprovalTests.MethodName"
# Publish the WPF app (framework-dependent, win-x64, same flags as the release pipeline)
dotnet publish .\CSharpCodeAnalyst\CSharpCodeAnalyst.csproj -c Release -r win-x64 -o publish `
--self-contained false -p:PublishSingleFile=false -p:SatelliteResourceLanguages=enCommand-line (headless) validation mode — triggered when more than one CLI arg is passed, so App.OnStartup skips the UI and exits with a status code:
CSharpCodeAnalyst.exe -validate -sln:<path.sln> -rules:<path.txt> [-log-console] [-log-file:<file>] [-out:<file>]
Exit codes: 0 = clean, 1 = violations, 2 = validation failed. See Documentation/command-line-arguments.md.
Debug shortcut: passing a single arg -load:<project.json> auto-loads a saved project after the UI starts (see App.LoadProjectFileFromCommandLineAsync).
Five projects wired together in CSharpCodeAnalyst.sln:
CodeGraph/— pure, UI-free domain model. Contains theCodeElement/Relationship/CodeGraphgraph types (Graph/), graph algorithms (Algorithms/Cycles,Algorithms/Metrics,Algorithms/Partitioning), exporters (Export/— DGML, DSI, PlantUML, JSON), andExploration/CodeGraphExplorer(traversal queries used from the UI context menus). No WPF dependencies — reference this project from tests and tools.CodeParser/— Roslyn front-end that turns an.slnor.csprojinto aCodeGraph. Entry point:Parser.ParseAsync(path). Works in two passes:HierarchyAnalyzerfinds code elements and parent/child links, thenRelationshipAnalyzer.AnalyzeRelationshipsMultiThreadedwalks method and lambda bodies to build relationships.Initializer.InitializeMsBuildLocator()must be called once before any parse (bothApp.StartUiand the test fixtureInitdo this).CSharpCodeAnalyst/— WPF front-end. Organized by feature underFeatures/(CycleGroups,Graph,Tree,AdvancedSearch,Analyzers/ArchitecturalRules,Analyzers/EventRegistration,Ai,Import,Export,Metrics,Partitions,Refactoring,Gallery,Help,Info). Cross-cutting infrastructure lives inShared/(messaging, notifications, data grid, search, filter, WPF helpers).Configuration/holdsAppSettings(fromappsettings.json),UserPreferences(persisted touserSettings.json), andAiCredentialStorage. Persistence of saved projects is inPersistence/(JSON, with DTOs underDto/).Tests/(project nameCodeParserTests) — NUnit suite.ApprovalTests/parses theTestSuite/C# solution once per fixture and asserts on the resulting graph;UnitTests/covers cycles, exploration, export, search, architectural rules, etc.ApprovalTestTool/— standalone console app that clones external repos listed inRepositories.txt, parses each at a pinned commit, hashes the graph dump, and diffs against references. Used to catch parser regressions on real codebases; not part of the CI test run.
TestSuite/ is a handcrafted C# solution used purely as parser input for the approval tests. Do not consume it from production code — it is intentionally full of odd language constructs. ReferencedAssemblies/ contains the MSAGL DLLs referenced directly by CSharpCodeAnalyst.csproj and Tests.csproj (MSAGL is not on NuGet for the versions used here).
Every project inherits a Microsoft.Build.Framework PackageReference with ExcludeAssets="runtime". This is load-bearing: Microsoft.Build.Locator loads MSBuild from the installed SDK at runtime, and copying the NuGet-provided MSBuild DLLs into bin/ causes RPC_E_CALL_REJECTED and other loader failures. When upgrading Roslyn / MSBuild packages, keep the exclusion in place and bump the version comment in Directory.Build.props to match the transitive version from Microsoft.Build.Locator.
Parser.ParseSolutionInternal runs HierarchyAnalyzer → RelationshipAnalyzer → InsertGlobalNamespaceIfUsed. The global-namespace insertion normalizes assemblies that contain types directly at the root (e.g. test assemblies with generated Main) so that cycle detection always has a shared ancestor above Namespace rather than at Assembly. Preserve this invariant if you touch the post-processing.
The UI is not built on a DI container. App.StartUi wires up singletons manually (one MessageBus, one CodeGraphExplorer, one GraphViewer, etc.) and injects them into view models. Cross-view-model communication goes through Shared/Messages/MessageBus.cs (Publish/Subscribe on strongly-typed message records in Shared/Messages/). When adding a new cross-feature interaction, prefer defining a new message type over introducing direct view-model references.
Features/Graph/ wraps Microsoft Automatic Graph Layout (MSAGL). MsaglHierarchicalBuilder vs. MsaglFlatBuilder produce the graph for nested vs. flat views; GraphViewer hosts the MSAGL WPF control and routes clicks/context menus back to commands implementing ICodeElementContextCommand / IRelationshipContextCommand / IGlobalCommand. The ~200-element soft limit mentioned in the README is enforced via AppSettings.WarningCodeElementLimit.
Features/Ai/AiClient.cs talks to any OpenAI-compatible endpoint (including Anthropic, Ollama). Credentials are stored via Configuration/AiCredentialStorage. The service is stateless and is invoked from the cycle-group UI to summarize a cycle.
Tests/ApprovalTests/ApprovalTestBase uses [OneTimeSetUp] and a static Init class to parse TestSuite.sln exactly once per test run (path is relative: ..\..\..\..\TestSuite\TestSuite.sln from the test binary). Expected values are large HashSet<string> literals inline in each fixture; when the parser legitimately changes output, dump the actual set with ApprovalTestBase.DumpRelationships / DumpCodeElements and paste it back in. Do not mock the parser — these tests are the safety net for Roslyn-version upgrades.
Features/Refactoring/ mutates the in-memory CodeGraph directly (move / delete elements, cut edges) and there is no undo. Any code path that offers these operations should save or warn first — see existing command handlers before adding new ones.
.editorconfigis authoritative and ReSharper-tuned: braces required on allif/foreach/while, max line length 199, expression-bodied accessors preferred,internalfirst in modifier order. Analyzer severities default tonone— do not add warning-as-error enforcement without discussion.- Nullable reference types are enabled everywhere; honour the annotations rather than suppressing with
!. - Namespaces match folders.
CodeGraph.Graph.CodeGraphis a class inside theCodeGraphassembly — fully-qualify it (CodeGraph.Graph.CodeGraph) in places where the namespace/type collision is ambiguous; existing code already does.