This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
# Restore dependencies
dotnet restore ProjGraph.slnx
# Build
dotnet build ProjGraph.slnx
# Run all tests
dotnet test ProjGraph.slnx
# Run a specific test project
dotnet test tests/ProjGraph.Tests.Unit.ClassDiagram
# Run a specific test class
dotnet test tests/ProjGraph.Tests.Unit.ClassDiagram --filter "ClassAnalysisDepthTests"
# Run CLI locally
dotnet run --project src/ProjGraph.Cli -- visualize ./ProjGraph.slnx
dotnet run --project src/ProjGraph.Cli -- stats ./ProjGraph.slnx
# Run MCP server locally
dotnet run --project src/ProjGraph.McpBuild enforces TreatWarningsAsErrors=true and EnforceCodeStyleInBuild=true. XML documentation is required on all public APIs.
ProjGraph is a .NET 10 tool ecosystem with two entry points — a CLI (Spectre.Console.Cli) and an MCP server (ModelContextProtocol.Server over stdio) — both backed by a shared library layer.
Cli ──┐
├──► Lib ──► Lib.Core ──► Core
Mcp ──┘ ├──► Lib.Dependencies ──► Lib.Core
├──► Lib.ClassDiagram ──► Lib.Core
└──► Lib.EntityFramework ──► Lib.CoreProjGraph.Core— Pure domain models (SolutionGraph,ClassModel,EfModel,SolutionStats) and exceptions. No dependencies.ProjGraph.Lib.Core— Cross-cutting abstractions (IFileSystem,IOutputConsole,ICompilationFactory,IDiagramRenderer<T>), solution parsers (.sln/.slnx), and infrastructure utilities. Also hosts theTarjanSccAlgorithm(inDomain/Algorithms/) used for cycle detection.ProjGraph.Lib.Dependencies— Builds dependency graphs from solution/project files by parsing them directly with the in-house parsers inLib.Core(SlnParseronMicrosoft.VisualStudio.SolutionPersistence,SlnxParserandProjectParseronSystem.Xml.Linq; nothing is evaluated); computes stats usingTarjanSccAlgorithmfor cycle detection.ProjGraph.Lib.ClassDiagram— Roslyn-based C# class hierarchy analysis. Accepts a file or directory; optionally discovers related types across the workspace viaIWorkspaceTypeDiscovery.ProjGraph.Lib.EntityFramework— Roslyn semantic analysis of EF CoreDbContextfiles andModelSnapshotfiles; includes a multi-class Fluent API parser.ProjGraph.Lib— Composition root only. ExposesAddProjGraphLib()which wires all sub-library services via DI.
Use-case pattern — Each feature library organises logic as:
Application/
├── IServiceInterface.cs
├── ServiceImplementation.cs
└── UseCases/
└── SpecificUseCase.csRendering pipeline — Parse → Model → Render. Models are pure records in ProjGraph.Core. IDiagramRenderer<T> implementations are resolved by keyed DI (each renderer exposes a Format property). Multiple renderers can exist per model type (tree, flat, Mermaid).
MCP stdout safety — The MCP server overrides IOutputConsole with NullOutputConsole to prevent ANSI markup from leaking onto the JSON-RPC stdio transport.
Tool packaging — ProjGraph.Cli and ProjGraph.Mcp set PublishAot=true and ToolPackageRuntimeIdentifiers, so a plain dotnet pack builds only the pointer package. Each native package needs dotnet pack -r <rid> on a matching OS (Alpine for linux-musl-*), and the any fallback needs dotnet pack -r any -p:PublishAot=false. Because PublishAot is set, plain JIT builds also get AOT feature switches (e.g. IsDynamicCodeSupported=false) in their runtimeconfig.json. .github/workflows/pack.yml builds every package.
New feature checklist:
- Add domain models to
ProjGraph.Coreif needed. - Implement in the appropriate
Lib.*project following the use-case pattern. - Register services in that library's
DependencyInjection.cs. - Expose via CLI command (
src/ProjGraph.Cli/Commands/) and/or MCP tool (src/ProjGraph.Mcp/Program.cs). - Add unit and integration tests.
| Project | Purpose |
|---|---|
Tests.Unit.* |
Unit tests per library |
Tests.Integration.Cli |
CLI end-to-end tests |
Tests.Integration.Mcp |
MCP tool integration tests |
Tests.Contract |
MCP contract validation & DI wiring |
Tests.Smoke.Aot |
Native AOT vs JIT parity for the installed CLI and MCP tools (skipped unless PROJGRAPH_SMOKE_* is set; .github/scripts/native-tool-smoke.sh runs it in the aot-smoke CI job and for every RID in pack.yml) |
Tests.Shared |
Shared helpers (TestDirectory, TestPathHelper) |
Releases are triggered by pushing a v* tag. publish.yml builds and tests with -p:Version from the tag, then calls pack.yml. That workflow packs and smoke-tests the Native AOT tool packages on a matching runner for each RID (Alpine for linux-musl-*) and packs the libraries, the any fallbacks, and the two pointer packages. publish then pushes everything except the pointer packages to NuGet.org and GitHub Packages, waits until NuGet.org lists all 14 tool packages, pushes the pointer packages, creates a GitHub Release, and submits to the MCP Registry via mcp-publisher. Pre-release tags (containing -, e.g. v1.2.0-beta.1) get a GitHub pre-release and skip the MCP Registry, which would otherwise rank them "latest". See ARCHITECTURE.md ("Tool Packages") before changing ToolPackageRuntimeIdentifiers. The MCP Registry ownership comment (<!-- mcp-name: io.github.HandyS11/projgraph -->) must remain at the end of src/ProjGraph.Mcp/README.md.