nspect
A Rust CLI that analyzes the structure of C# projects and solutions: dependency graphs, package references, version conflicts, structural metrics (LOC / members / cyclomatic complexity), and a best-effort cross-check between declared packages and what the source actually imports.
Not a compiler, not Roslyn. Fast, read-only, works from the filesystem — no NuGet restore, no MSBuild evaluation.
Install
From a release binary
Linux / macOS:
curl -fsSL https://github.com/vivainio/nspect/releases/latest/download/nspect-linux-x86_64-musl -o ~/.local/bin/nspect && chmod +x ~/.local/bin/nspect
macOS (Apple Silicon):
curl -fsSL https://github.com/vivainio/nspect/releases/latest/download/nspect-macos-aarch64 -o ~/.local/bin/nspect && chmod +x ~/.local/bin/nspect
Windows (PowerShell):
iwr https://github.com/vivainio/nspect/releases/latest/download/nspect-windows-x86_64.exe -OutFile ~/.local/bin/nspect.exe
Via pip
pip install nspect
From source
git clone https://github.com/vivainio/nspect
cd nspect
cargo install --path .
Commands
nspect scan <path>
List every project reachable from <path> (a repo root, a .sln, or a .csproj) with its SDK style, target framework(s), and package/project refs.
$ nspect scan ./my-repo
Found 12 project(s)
┌──────────────┬─────┬────────────────────────┬──────┬──────────┐
│ Project │ SDK │ TargetFramework(s) │ Pkgs │ ProjRefs │
╞══════════════╪═════╪════════════════════════╪══════╪══════════╡
│ Web.Api │ sdk │ net8.0 │ 14 │ 3 │
│ Domain │ sdk │ net8.0, netstandard2.0 │ 2 │ 0 │
...
Add --format json for machine output.
nspect graph <path>
Emit a project-to-project dependency graph as DOT, Mermaid, JSON, or a text summary.
nspect graph ./my-repo --format dot | dot -Tsvg > graph.svg
nspect graph ./my-repo --format mermaid > graph.mmd
nspect graph ./my-repo --format text
Package nodes are off by default — on large monoliths they drown out the project structure. Add --packages to include them.
nspect atlas <path>
Emit a structural snapshot of the repo as YAML (default) or JSON: areas, projects with fan-in/fan-out/layer, and internal vs. external references.
nspect atlas ./my-repo # YAML to stdout
nspect atlas ./my-repo --format json # JSON
nspect atlas ./my-repo --check # embed findings (see below) under `findings:`
nspect atlas ./my-repo --output-dir ./out # writes multiple artifacts (see below)
With --output-dir, the tree-sitter source scan runs and these artifacts are written side by side:
| File | Contents |
|---|---|
atlas.yaml |
Project graph (areas, fan-in/fan-out, layers, refs). Each project also gains a weight: block with aggregate types, loc, members, complexity. |
classes.yaml |
Declared types per project, grouped by namespace and bucketed by kind (class, interface, struct, record, record_struct, enum, delegate). Nested types keep a dotted local path (e.g. Outer.Inner). |
metrics.yaml |
Same shape as classes.yaml but values are {loc, members, complexity, methods} per type, plus a per-project totals: block. |
checks.yaml |
Only written with --check. The findings list (see below) as a standalone artifact. |
tips.yaml |
Soft architectural suggestions (e.g. merge_candidates). Non-authoritative — meant for human review, not CI gates. |
Without --output-dir, only the atlas itself is emitted (to stdout) and no source scan is performed — unless --check forces it to run the package-ref heuristics.
--check findings
When --check is on, atlas.yaml gains a findings: array (and with --output-dir, a sibling checks.yaml). CI gates can grep for error-severity kinds.
| Finding | Severity | What it means |
|---|---|---|
cycle |
error | A project-to-project reference cycle. |
version_conflict |
error | Same package declared with different versions across projects. |
unresolved_project_ref |
warning | A <ProjectReference> that doesn't resolve on disk. |
unused_package_ref |
warning | A <PackageReference> whose namespaces never appear in any using of the project. Skips test runners, analyzers, and runtime shims by default. |
undeclared_usage |
warning | A using X.Y.Z; that doesn't match any declared package or project ref. Advisory only — noisy on legacy codebases that rely on transitive DLL discovery. |
orphan_project |
info | A project with no incoming or outgoing project refs. |
nspect metrics <path>
Fast text summary of structural metrics — runs the tree-sitter pass, prints a per-project table plus the top methods by cyclomatic complexity. Scope is whatever csprojs live under <path>, so you can point it at a single .csproj, a subdirectory, or the whole repo:
$ nspect metrics tests/fixtures/sourcescan --top 5
project types loc members complexity
--------------------------------------------------
App 1 9 1 0
--------------------------------------------------
TOTAL 1 9 1 0
top 1 methods by complexity:
0 6 App.Program.Main
When the scope contains more than one project, each method in the top-N section is prefixed with project:: to disambiguate.
Flags:
--top <N>— how many top methods to list (default 20,0disables the section)--project <name>— restrict the methods section to a single project (exact, suffix, or substring match)
Metric definitions:
- loc — source lines spanned by the type / method declaration.
- members — direct methods, properties, fields, ctors, events, indexers. Nested types are not counted as members.
- complexity — McCabe-ish branch count:
if,while,for,foreach,do,case,catch, ternary?:,whenclauses. Logical&&/||are currently not counted. Branches inside nested types count toward the enclosing type.
nspect ts-dump <file.cs>
Debug aid. Shows the extracted usings, top-level named children of the parse tree with line ranges, and (with --sexp) the full tree-sitter S-expression annotated with leaf source text:
(class_declaration
(modifier "public")
name:
(identifier "Greeter")
body:
(declaration_list
(method_declaration ...)))
Useful for writing new heuristics against the CST.
What it handles
- SDK-style csproj —
<PackageReference>,<ProjectReference>,TargetFramework(s),AssemblyName - Legacy csproj —
<Reference>assembly refs are counted as namespace providers .slnfiles — project list (the format is not XML; parsed directly)- Central Package Management — walks up for
Directory.Packages.propsand resolves version-lessPackageReferenceentries - Multi-targeting — captures
TargetFrameworks="net8.0;netstandard2.0"as a list - Malformed csprojs — skipped with a warning instead of aborting the scan
What it doesn't handle
- MSBuild property evaluation.
$(Foo)references are recorded as-is; nothing is expanded. Attempting to evaluate MSBuild correctly is a rabbit hole. Directory.Build.props/targets. Presence is not currently merged into project metadata. Flagged for a future milestone.- Transitive DLL discovery via HintPath. Legacy .NET Framework monoliths rely on
packages/*/lib/*.dllbeing found through a chain of HintPaths. Theundeclared_usagefinding does not trace these, which is why it's noisy on legacy codebases. - Type resolution. The source scan is textual.
using Foo.Bar;produces the string"Foo.Bar"; whether that's a namespace or a static type is not determined. - NuGet restore.
nspectanalyzes what's declared, not what would resolve.
Performance
On a ~790-csproj monolith:
| Command | Time |
|---|---|
nspect scan (parse all csprojs + CPM) |
~0.3 s |
nspect graph |
~0.3 s |
nspect atlas |
~0.3 s |
nspect atlas --check (tree-sitter across ~all .cs files) |
~28 s |
nspect atlas --output-dir ... (full source scan + per-type metrics) |
~28 s |
License
MIT
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distributions
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file nspect-0.1.0-py3-none-win_amd64.whl.
File metadata
- Download URL: nspect-0.1.0-py3-none-win_amd64.whl
- Upload date:
- Size: 2.5 MB
- Tags: Python 3, Windows x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ebf9422185dc17f6d2303a3a83f001e36d0ae88209ce5f9b76e0767d034b627b
|
|
| MD5 |
d22457fe59b2aa782b1504cb4a14744e
|
|
| BLAKE2b-256 |
fd87b7cd21f83108ed73b3bf2a8e2f26d3f35487d017ad8942c0490f159dcec8
|
Provenance
The following attestation bundles were made for nspect-0.1.0-py3-none-win_amd64.whl:
Publisher:
ci.yml on vivainio/nspect
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
nspect-0.1.0-py3-none-win_amd64.whl -
Subject digest:
ebf9422185dc17f6d2303a3a83f001e36d0ae88209ce5f9b76e0767d034b627b - Sigstore transparency entry: 2770672530
- Sigstore integration time:
-
Permalink:
vivainio/nspect@371352080a8c88dec8cb4e56153e0160cc075d98 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/vivainio
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@371352080a8c88dec8cb4e56153e0160cc075d98 -
Trigger Event:
release
-
Statement type:
File details
Details for the file nspect-0.1.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: nspect-0.1.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 2.6 MB
- Tags: Python 3, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f7110b602cdcc5dac2ff70c228eb4c48021440cde84ef6b0923b29433fafc117
|
|
| MD5 |
3be466e9369b4bc407d0785ec4a6604d
|
|
| BLAKE2b-256 |
fa3d50c28b7241f1935afac0e594745887a7d6d7d79782892094439f70f8126e
|
Provenance
The following attestation bundles were made for nspect-0.1.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:
Publisher:
ci.yml on vivainio/nspect
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
nspect-0.1.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl -
Subject digest:
f7110b602cdcc5dac2ff70c228eb4c48021440cde84ef6b0923b29433fafc117 - Sigstore transparency entry: 2770672695
- Sigstore integration time:
-
Permalink:
vivainio/nspect@371352080a8c88dec8cb4e56153e0160cc075d98 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/vivainio
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@371352080a8c88dec8cb4e56153e0160cc075d98 -
Trigger Event:
release
-
Statement type:
File details
Details for the file nspect-0.1.0-py3-none-macosx_11_0_arm64.whl.
File metadata
- Download URL: nspect-0.1.0-py3-none-macosx_11_0_arm64.whl
- Upload date:
- Size: 2.4 MB
- Tags: Python 3, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
18a2f02f0839565237bf47877e3d61161cd265915147dc631ae34faca54603a2
|
|
| MD5 |
92a1df0a46f7f87b23344a0a8ba91191
|
|
| BLAKE2b-256 |
724df1764af0e5688161399599e94593df8d9bb6982166507043b51c0a6550c1
|
Provenance
The following attestation bundles were made for nspect-0.1.0-py3-none-macosx_11_0_arm64.whl:
Publisher:
ci.yml on vivainio/nspect
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
nspect-0.1.0-py3-none-macosx_11_0_arm64.whl -
Subject digest:
18a2f02f0839565237bf47877e3d61161cd265915147dc631ae34faca54603a2 - Sigstore transparency entry: 2770672432
- Sigstore integration time:
-
Permalink:
vivainio/nspect@371352080a8c88dec8cb4e56153e0160cc075d98 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/vivainio
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@371352080a8c88dec8cb4e56153e0160cc075d98 -
Trigger Event:
release
-
Statement type:
File details
Details for the file nspect-0.1.0-py3-none-macosx_10_12_x86_64.whl.
File metadata
- Download URL: nspect-0.1.0-py3-none-macosx_10_12_x86_64.whl
- Upload date:
- Size: 2.5 MB
- Tags: Python 3, macOS 10.12+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
da5a6f61f00a2ac2c6992ab008101e2fb2dfe73d597100e2ec4739cf184203ba
|
|
| MD5 |
ea44a0f2f1da337129ac09ea1dcc40c0
|
|
| BLAKE2b-256 |
e4350a4f194c03ce95718b97f6f87db9e53d7eb3a75fd01dceacb23d95b088c2
|
Provenance
The following attestation bundles were made for nspect-0.1.0-py3-none-macosx_10_12_x86_64.whl:
Publisher:
ci.yml on vivainio/nspect
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
nspect-0.1.0-py3-none-macosx_10_12_x86_64.whl -
Subject digest:
da5a6f61f00a2ac2c6992ab008101e2fb2dfe73d597100e2ec4739cf184203ba - Sigstore transparency entry: 2770672589
- Sigstore integration time:
-
Permalink:
vivainio/nspect@371352080a8c88dec8cb4e56153e0160cc075d98 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/vivainio
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@371352080a8c88dec8cb4e56153e0160cc075d98 -
Trigger Event:
release
-
Statement type: