Skip to content

Repository files navigation

unilyze

CI Code Health NuGet Homebrew Scoop

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

demo.mp4

Type dependency graph with Code Health badges

Live demo (unilyze analyzing its own source): https://bigdra50.github.io/unilyze/demo/

Documentation: https://bigdra50.github.io/unilyze/

Requirements

  • No .NET installation is required for Homebrew, Scoop, or direct-download binaries
  • The dotnet tool channel 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.

Quick Start

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.json

Release 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 ./unilyze

Run in a Unity project directory to open the analysis results in your browser:

cd ~/MyUnityProject
unilyze

Tutorials

Step-by-step walkthroughs for the highest-value workflows:

  • CI quality gate — badge gates, diff --fail-on-regression, PR markdown comments, --base-ref baselines
  • Agent integrationskills install, evidence packs (query), refactor-loop and quality-audit workflows
  • Continuous refactoringhotspot prioritization, snapshot history, trend interpretation

Usage

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 level

Subcommands

unilyze 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 skills

Run 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).

Status Line Integration

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.

Badges

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.svg

The 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.

Private repositories

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.

GitHub Action

- uses: bigdra50/unilyze@v1
  with:
    project-path: .
    metric: codehealth
    fail-under: "7.0"
    base-ref: origin/main
    fail-on-regression: "true"
    baseline: .unilyze/baseline.json

Full workflow YAML, input table, and badges.yml publishing pattern: docs/ci-integration.md.

Why unilyze

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

Analysis Levels

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 analysis

--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 level

Requirements 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/ with actions/cache keyed on lockfiles and .unilyze.json to 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).

Configuration

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)
// .unilyze.json
{
  "excludeDirs": ["Assets/Plugins", "Assets/ThirdParty"],
  "profile": "unity",
  "smells": { "LongMethod": { "lines": 100, "criticalLines": 200 } },
  "rules": { "UNI011": "off", "UNI009": "off" }
}

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.

Reference analysis opt-ins (.NET)

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 repos

Equivalent .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.

Suppressing findings

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-line suppresses listed rules on the following line (detector smells with line numbers).
  • unilyze-disable in 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 UNI009 print a stderr warning and are ignored.
  • Suppressed smells stay in JSON with "suppressed": true, increment root suppressedCount, and appear in SARIF with suppressions { "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.

Assembly mapping

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

Metrics

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.

Detection capabilities

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.

Output Formats

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)

In-browser read-only source viewer

Diff Viewer

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.

Diff viewer with degradation halos and metric badges

unilyze diff before.json after.json --fail-on-regression
unilyze diff --base-ref origin/main after.json -f markdown --changed-only

Agent Workflow

See 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

Known Limitations

  • 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).

License

MIT

About

Unity + Analyze — CLI tool for static analysis, code metrics, and dependency visualization of Unity C# projects

Resources

Contributing

Security policy

Stars

4 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages