C# static analysis that runs on .cs and .asmdef alone — no MSBuild or sln required. Computes churn × complexity hotspots from git history, live-updates metrics in the browser on save (unilyze serve), and ships a self-documenting CLI (metrics, schema, query) with bundled AI agent skills. General C# projects are supported via .csproj discovery and semantic analysis when a solution is present.
For build, test, and release information, see README.dev.md.
demo.mp4
Live demo (unilyze analyzing its own source): https://bigdra50.github.io/unilyze/demo/
Documentation: https://bigdra50.github.io/unilyze/
- No .NET installation is required for Homebrew, Scoop, or direct-download binaries
- The
dotnet toolchannel requires .NET 8.0 or later
.NET version support policy: The dotnet tool channel targets every TFM from the oldest supported LTS up to the latest LTS, with no gaps. EOL is not an exclusion criterion; the floor LTS is raised only once it reaches EOL. As of 2026-06, supported TFMs are net8.0, net9.0, and net10.0.
For users who already have .NET, the global tool remains the recommended installation:
dotnet tool install --global Unilyze
Install a self-contained binary without .NET:
# macOS or Linux
brew install bigdra50/tap/unilyze
# Windows
scoop install https://github.com/bigdra50/unilyze/releases/latest/download/unilyze.jsonRelease archives for osx-arm64, osx-x64, linux-x64, and win-x64 are also available from GitHub Releases.
Verify downloads with the attached SHA256SUMS file.
For a macOS archive downloaded through a browser, remove the quarantine attribute before the first run:
xattr -d com.apple.quarantine ./unilyzeRun in a Unity project directory to open the analysis results in your browser:
cd ~/MyUnityProject
unilyzeStep-by-step walkthroughs for the highest-value workflows:
- CI quality gate — badge gates,
diff --fail-on-regression, PR markdown comments,--base-refbaselines - Agent integration —
skills install, evidence packs (query), refactor-loop and quality-audit workflows - Continuous refactoring —
hotspotprioritization, snapshot history,trendinterpretation
unilyze # Analyze and open in browser
unilyze -p ~/MyUnityProject # Specify project path
unilyze -p ~/MyUnityProject -o graph.html # Save HTML + JSON
unilyze -p ~/MyUnityProject -f json -o result.json # JSON output
unilyze -p ~/MyUnityProject -f sarif -o report.sarif # SARIF (stable fingerprints)
unilyze -p ~/MyUnityProject --profile unity # Unity role-aware smell thresholds
unilyze -p ~/MyUnityProject --baseline .unilyze/baseline.json # Suppress known smells
unilyze -p ~/MyUnityProject --level core # Pin analysis levelunilyze config list # Show/manage configuration
unilyze baseline create -p . # Snapshot smells for zero-new-violations
unilyze diff <before.json> <after.json> # Compare snapshots (JSON)
unilyze diff --base-ref origin/main after.json -f markdown --fail-on-regression
unilyze diff <before.json> <after.json> -o diff.html --changed-only
unilyze hotspot -p ~/MyUnityProject # Git churn × complexity
unilyze dup -p ~/MyUnityProject # Token-normalized clone detection
unilyze trend <dir-of-jsons> # Quality trend across snapshots
unilyze trend <dir-of-jsons> -o trend.html # Self-contained HTML trend charts
unilyze query --worst 5 -i snapshot.json # Per-type evidence packs
unilyze calibrate <dir-of-jsons> -o thresholds.json # Derive threshold candidates
unilyze statusline -p ~/MyUnityProject # Compact summary for status line
unilyze badge -p ~/MyUnityProject -o badge.json # shields.io endpoint JSON
unilyze metrics # Metric definitions & thresholds
unilyze schema # JSON field reference
unilyze skills install --claude --cursor # Install agent skillsRun unilyze --help for all options. JSON output includes projectKind (unity | dotnet) and profile.
Exit codes (all commands): 0 success / gate passed, 1 usage error, 2 quality gate failed (badge with --fail-under / --fail-over, or diff with --fail-on-regression).
unilyze statusline outputs a one-line code health summary (e.g. CH:9.8/5.9 W:9.4 T:7.2 111smells 🔴1 📦66). Pass --show-mi to append the reference MI metric. It never blocks: the cached line prints immediately while a stale or missing cache refreshes in the background, so it is safe for Claude Code's status bar. Details: docs/statusline.md.
unilyze badge -p . --metric codehealth --fail-under 7 # CI gate example
unilyze badge -p . --metric energy --fail-over 1.0 # Unity hot-path smell density proxy
unilyze badge -p . --format svg -o .github/badges/codehealth.svgThe energy metric is a static source-code proxy, not measured energy or power.
See the CI quality gate tutorial and docs/ci-integration.md for endpoint vs SVG badges, quality-gate semantics, GitHub Actions, diff regression gates, and monorepo --projects batch analysis.
Generate SVG with unilyze badge --format svg, commit under .github/badges/, and reference via a relative path — shields.io endpoints do not work in private repos. See docs/ci-integration.md#private-repositories.
- uses: bigdra50/unilyze@v1
with:
project-path: .
metric: codehealth
fail-under: "7.0"
base-ref: origin/main
fail-on-regression: "true"
baseline: .unilyze/baseline.jsonFull workflow YAML, input table, and badges.yml publishing pattern: docs/ci-integration.md.
| unilyze | NDepend | SonarQube | CodeScene | Qodana | |
|---|---|---|---|---|---|
| Unity setup | .cs/.asmdef alone, Unity DLLs resolved progressively |
VS solution / compiled assemblies required | MSBuild project required (SonarScanner for .NET) | Git repo + service onboarding; no Unity-specific analysis | .sln/.csproj pre-generated (Rider sync script) |
| Churn × complexity hotspots | unilyze hotspot; method-level via --methods |
None (trend baselines only) | None ("Security Hotspot" is unrelated) | File-level; function-level in X-Ray | None |
| Agent integration | Bundled skills (Claude/Codex/Cursor/Gemini/Windsurf), self-documenting CLI (metrics/schema/query), stable JSON |
NDepend MCP | SonarQube MCP Server | CodeScene MCP | None found in survey |
unilyze resolves an analysis level based on which Unity DLLs it can locate. Pin with --level <syntax|core|full|complete>. See docs/ci-integration.md#analysis-levels-in-ci for the full table and CI caveats.
--incremental caches per-type analysis results under <project>/.unilyze/cache/syntax/v1/ and works at every analysis level. Warm syntax-level runs re-parse only changed files. Warm semantic runs (core/full/complete) additionally reuse per-type enrichment (LCOM/CBO/DIT/RFC + code smells) with reverse-dependency-index (RDI) invalidation: a body-only edit re-enriches just the edited types; a signature, using-directive, member, or base/interface-list change re-enriches the changed types plus their recorded dependents (and inheritance descendants where bindings can shift); type or file adds/deletes and global-using changes conservatively re-enrich everything. Cross-file aggregates (dependencies, coupling, cycles, rank) are always recomputed in full, so warm output is byte-identical to a full run.
unilyze -p . --incremental -f json -o result.json # any analysis levelRequirements and limits:
- Cannot be used with
-i/--input. - Cache invalidates when
toolVersion,metricsVersion, preprocessor defines, thresholds/profile/rules, exclude dirs, assembly layout, resolved references, target framework, or analysis level change. - CI: persist
.unilyze/cache/withactions/cachekeyed on lockfiles and.unilyze.jsonto reuse warm analysis across runs.
The cache directory includes .unilyze/cache/.gitignore containing * (auto-created).
unilyze serve always analyzes at the semantic level with the incremental cache warm; pass serve --verify-incremental <N> to additionally run a full analysis every N generations and log any divergence to stderr (off by default — it doubles analysis cost on the sampled generations).
Settings merge additively from global config (~/.config/unilyze/config.json), project .unilyze.json, and CLI flags.
| Scope | Path |
|---|---|
| Global | $XDG_CONFIG_HOME/unilyze/config.json |
| Project | <project-root>/.unilyze.json |
| CLI | --exclude-dir <dir> (repeatable) |
UNI009 off disables cyclic-dependency detection entirely. Other rule IDs map to smell kinds and filter JSON, SARIF, badge, and statusline output. Threshold keys are case-insensitive; defaults are in docs/metrics.md (drift-tested) and unilyze metrics.
For general .NET projects, semantic metrics (CBO, DIT, boxing) use BCL references by default. Optional flags deepen resolution without MSBuild:
unilyze -p . --resolve-nuget # NuGet compile assemblies from obj/project.assets.json (after dotnet restore)
unilyze -p . --include-generated # EmitCompilerGeneratedFiles output (compilation only; metrics unchanged)
unilyze -p . --resolve-nuget --tfm net8.0 # Pin target framework for multi-TFM reposEquivalent .unilyze.json keys: "resolveNuget", "includeGenerated", "targetFramework". Enabled settings are echoed in JSON output. Compare snapshots only with identical opt-in settings — see docs/metrics.md.
Three suppression mechanisms compose without double-counting:
| Mechanism | Scope | When to use |
|---|---|---|
| Inline comment | Single occurrence | Justified one-off (intentional facade, measured-safe boxing site) |
"rules": { "UNI011": "off" } |
Rule-wide | Rule is noisy for the whole project |
--baseline / baseline create |
Project snapshot | Brownfield freeze; gate on new violations only |
Inline directives (ESLint-style):
// unilyze-disable-next-line UNI014 -- top-level guard, intentional
catch { }
// unilyze-disable UNI002
void MeasuredLongMethod() { /* ... */ }unilyze-disable-next-linesuppresses listed rules on the following line (detector smells with line numbers).unilyze-disablein the leading trivia of a type or method declaration suppresses listed rules for that declaration's scope.- Omit rule IDs to suppress all rules in scope. Unknown rule IDs and
UNI009print a stderr warning and are ignored. - Suppressed smells stay in JSON with
"suppressed": true, increment rootsuppressedCount, and appear in SARIF withsuppressions{ "kind": "inSource" }. They are excluded from statusline, badge gates, and diff regression counts.
Known constraints: metric-based smells (UNI001–UNI008, UNI010) match directives by method or type name, so a directive on one overload suppresses the smell for all same-name overloads; detector smells (UNI011–UNI025) use line positions and distinguish overloads. For partial types, place type-scope directives on the declaration indexed by unilyze (see docs/metrics.md). UNI009 (cyclic dependency) is config-only.
| Project kind | Discovery | One assembly per |
|---|---|---|
| Unity | .asmdef under Assets/ |
asmdef name |
| .NET | .csproj (solution-first, else recursive) |
csproj file name |
| Fallback | no asmdef / no csproj | single Assembly-CSharp |
ProjectReference items become assembly dependency edges. --prefix and --assembly filter assemblies the same way for asmdefs and csproj-derived names.
unilyze config add-exclude-dir Assets/Plugins
unilyze baseline create -p . -o .unilyze/baseline.json| Metric | Description | Granularity |
|---|---|---|
| Cognitive Complexity | SonarSource-compliant cognitive complexity | Method |
| Cyclomatic Complexity | McCabe 1976-compliant cyclomatic complexity | Method |
| Halstead D/E/B | Difficulty, Effort, EstimatedBugs | Method |
| LCOM-HS | Henderson-Sellers cohesion | Type |
| WMC | Weighted Methods per Class | Type |
| NOC / RFC / CBO / DIT | Chidamber-Kemerer suite | Type |
| Ca / Ce / Instability | Martin package metrics | Type |
| Maintainability Index | Halstead Volume + CycCC + LoC | Method |
| TypeRank | PageRank-based importance | Type |
| Code Health | Composite score (1.0 worst – 10.0 best) | Type |
| Abstractness / DfMS / Relational Cohesion | Assembly-level metrics | Assembly |
| Burst coverage / ECS type count | [BurstCompile] adoption on ECS systems/jobs |
Assembly |
Run unilyze metrics for definitions and thresholds. See docs/metrics.md for specifications and validation data.
Metric-threshold smells (God Class, Long Method, coupling, cohesion, etc.), performance analysis (boxing, closures, params arrays), exception-flow patterns, Unity frame-rate rules (UNI017–UNI021: hot-path API/LINQ/allocation/string concat, weak temporization), async/blocking rules (UNI022–UNI023), DOTS/ECS rules (UNI024–UNI025: missing [BurstCompile], managed IComponentData fields) with per-assembly burstCoverage, and DI container edge detection (VContainer, Zenject) — all configurable via .unilyze.json and --profile unity. Does not duplicate com.unity.entities source-generator diagnostics that fail the Editor build; see docs/metrics.md#dots--ecs. Thresholds are not duplicated here; see docs/metrics.md and unilyze metrics.
| Format | Use Case |
|---|---|
html |
Interactive dependency graph (lazy Cytoscape elements; dagre bundled; ELK Worker via CDN) |
json |
Agent integration, programmatic use |
sarif |
GitHub Code Scanning (stable fingerprints, rule help links) |
unilyze diff <before.json> <after.json> -o diff.html overlays metric deltas on the standard viewer (change badges, Changed only toggle, graph halos). Regression gates, markdown PR output, and --base-ref workflows: docs/ci-integration.md.
unilyze diff before.json after.json --fail-on-regression
unilyze diff --base-ref origin/main after.json -f markdown --changed-onlySee the agent integration tutorial.
unilyze (measure) → unilyze query (evidence) → fix → unilyze diff (verify)
unilyze query --worst 5 -i snapshot.json # evidence packs (md or -f json)
unilyze calibrate snapshots/ -o calibration.json # percentile thresholds for .unilyze.json
unilyze skills install --claude --codex --cursor
unilyze metrics && unilyze schema # self-documenting CLI- HTML graph works offline (Cytoscape and dagre bundled). ELK layout runs in a CDN-loaded Worker, falls back to main-thread ELK if Worker startup fails, and uses dagre when ELK is unavailable.
- Large graphs initially materialize only namespace nodes and types in the initially expanded namespace. Type nodes and edges are added on expansion and removed on collapse.
- Windows is covered by CI (windows-latest, net10.0).
MIT


