codaviz
A lightweight CLI that analyzes code complexity and emits a single, self-contained, interactive HTML report — a simpler, local alternative to SonarQube for spotting where to refactor. Python works out of the box; JavaScript/TypeScript, Go, Ruby, Rust, Java, and PHP are supported via an optional tree-sitter extra.
Point it at a repo and it ranks complexity hotspots, draws a treemap (sized by lines of code, colored by a metric you pick), lets you drill into any module to read its functions and source, and flags circular imports and over-threshold functions. No server, no database, no network — the report is one HTML file you can open offline or email to a teammate.
Full documentation lives in docs/src/ (built with Zensical: make docs).
Quick start
codaviz is a uv project. From a checkout:
uv sync # install (Python analysis)
uv sync --extra treesitter # + JS/TS, Go, Ruby, Rust, Java, PHP
uv run codaviz /path/to/project # writes ./report.html
open report.html # (macOS; use xdg-open on Linux)
Try it on codaviz itself:
uv run codaviz . && open report.html
To install it as a standalone command:
uv tool install . # Python only
uv tool install ".[treesitter]" # all languages
# then, anywhere: codaviz /path/to/project
Usage
codaviz [OPTIONS] [PATHS...]
PATHS Project directories to analyze (default: current dir).
Pass several to merge a workspace into one report.
-f, --format html | json | csv (default: html)
-o, --output FILE Output file
(html → report.html; json/csv → stdout unless set)
--no-source Omit embedded source snippets (smaller, shareable report)
--version Show version and exit
-h, --help Show help and exit
Examples:
codaviz ~/src/myapp # interactive report.html
codaviz ~/src/myapp -o myapp.html # custom output path
codaviz ~/src/myapp --no-source -o share.html # no source embedded
codaviz ~/src/myapp -f json > data.json # raw entity data
codaviz ~/src/myapp -f csv > data.csv # one row per package/module/function
codaviz packages/* # merge a workspace into one report
The report
- Treemap — packages and modules as nested tiles, sized by lines of code and colored by the selected metric (packages by their aggregate over all descendants; greener = better). Click a package to zoom in; a depth control caps how many levels show at once so large trees stay readable.
- Metric selector — switch between Maintainability index (default), max/total cyclomatic, max/total cognitive, and lines of code; the treemap, bar chart, and table all re-rank instantly.
- Hotspots table & bar chart — ranked by the selected metric, with LOC / MI / max & total cyclomatic / max & total cognitive / function count. A Modules / Packages toggle switches between per-module rows and package aggregates — so a complex package still stands out even when it's split into many small modules.
- Function detail — click a tile, bar, or row to see that module's functions, each with a CC (cyclomatic) and Cog (cognitive) badge, line number, and source snippet. Functions over either threshold get a "consider extracting" / "hard to follow" hint.
- Circular imports — modules that import each other (statically detected) are listed as cycles.
What gets analyzed
Inside a git repo, codaviz analyzes the source files git knows about — tracked and uncommitted — while honoring .gitignore (so .venv, build output, and ignored trees are skipped). Outside a repo, it walks the directory. Each file is dispatched by extension to the analyzer for its language. On top of that it always skips common noise: virtualenvs, caches, build/, dist/, node_modules/, site-packages/, migrations/, and test files (tests/, test_*.py, *_test.py, conftest.py) unless you opt in.
Languages
| Language | Metrics | Extensions |
|---|---|---|
| Python | CC, cognitive, MI, SLOC | .py |
| JavaScript / TypeScript | CC, SLOC | .js .jsx .mjs .cjs .ts .tsx .mts .cts |
| Go | CC, SLOC | .go |
| Ruby | CC, SLOC | .rb |
| Rust | CC, SLOC | .rs |
| Java | CC, SLOC | .java |
| PHP | CC, SLOC | .php .phtml |
Python works with no extra dependencies. Every other language uses tree-sitter and needs the optional treesitter extra (pip install codaviz[treesitter] / uv sync --extra treesitter); without it, those files are simply skipped. Maintainability index is Python-only and per-language cognitive complexity is planned, so non-Python modules report cyclomatic + SLOC and the report opens on a cyclomatic lens. A single report can mix languages.
Adding a language. codaviz uses a pluggy plugin system: a package contributes analyzers over the codaviz hook namespace and registers via the codaviz entry-point group. A new tree-sitter language is a small config subclass (its grammar + which node kinds count) — no new parsing code.
Metrics
| Metric | Meaning | Direction |
|---|---|---|
| Cyclomatic (CC) | McCabe complexity — branch/loop count + 1. Per function. Matches Ruff's C901 / python -m mccabe. |
higher = worse |
| Cognitive (Cog) | SonarSource cognitive complexity — penalises nesting, ignores shorthand humans read easily. The better "how hard to understand" signal. Per function. | higher = worse |
| Maintainability index (MI) | radon's 0–100 composite (≥20 = A/good, 10–19 = B, <10 = C). Per module. Kept as the familiar number. | lower = worse |
| SLOC | Source lines of code (excludes blanks/comments). | — (used for tile size) |
For Python, cyclomatic complexity comes from mccabe (so the numbers match Ruff), cognitive from cognitive_complexity, MI + SLOC from radon, and circular imports from a static ast import graph (no code is executed). For other languages, cyclomatic + SLOC are computed from the tree-sitter syntax tree — McCabe-family (decision points + 1), not tied to any specific external tool, so numbers are internally consistent per language but not claimed to match gocyclo/PMD/ESLint.
Configuration
Optional [tool.codaviz] table in the analyzed project's pyproject.toml:
[tool.codaviz]
exclude = ["generated/*.py", "vendor/**"] # extra glob patterns to skip
max-complexity = 15 # cyclomatic threshold for hints
max-cognitive = 15 # cognitive threshold for hints
treemap-depth = 2 # initial treemap depth (0 = all levels)
include-tests = false # set true to analyze test files too
Known limitations
- Nested defs: closures and methods of function-local classes are folded into their enclosing function's score rather than listed separately — matching how Ruff/mccabe report the outer function.
- Circular imports: resolution favors false negatives over false positives.
src/layouts, implicit namespace packages, and relative imports resolve correctly (names are computed relative to the detected source root); dynamic or conditional imports are not tracked. - Color scale: the badness ramp is green→amber→red with a "better → worse" legend and a numeric table as non-color channels; a fully colorblind-safe palette is a planned option.
Development
make test # uv run pytest
make lint # ruff check + format check + type checks
make format # ruff format + autofix
The test suite spans unit / integration / end-to-end tiers, including in-browser execution of the report's JavaScript via Node.
Status
Latest release 0.6.0; multi-language + plugin support has landed on main (see CHANGES.md, Unreleased). Stable and usable: hotspots, treemap (package-level aggregates, tunable depth), drill-down, cyclomatic + cognitive complexity, maintainability index, circular imports, threshold hints, src/-layout and multi-root/workspace analysis, seven languages behind a pluggy plugin seam, and HTML/JSON/CSV output. Planned next (rough order): a shared cognitive-complexity walker for the tree-sitter languages, churn-weighted hotspots (complexity × git change-frequency), and coupling metrics (afferent/efferent, instability). See notes/ for the vision, spec, and plans.
Non-goals
Security scanning (use Bandit), runtime profiling (use cProfile/scalene), and test coverage (use pytest-cov) are out of scope — codaviz focuses on structural complexity.
License
Apache License 2.0 — see LICENSE.
Metadata
Release files for codaviz 0.7.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| codaviz-0.7.0.tar.gz | 377.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| codaviz-0.7.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 767.5 kB
Release files / codaviz-0.7.0.tar.gz
| Download URL | codaviz-0.7.0.tar.gz |
|---|---|
| Size | 377.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
2d7a786168cef542817e4e6fc24c15b7f338baba280f67a0a3423be96f6c0950
|
|
BLAKE2b-256 checksum How to use checksums |
788667b7c53dc01bf28603b9485f1e0e70bb3ff8d9a1fed6442aa59d9d433b30
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.12 {"installer":{"name":"uv","version":"0.12.12","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / codaviz-0.7.0-py3-none-any.whl
| Download URL | codaviz-0.7.0-py3-none-any.whl |
|---|---|
| Size | 390.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
dd120752d91942d06cbf8cebc71c71975fbbaf2b441a0fa3185b1d728c1a0e2a
|
|
BLAKE2b-256 checksum How to use checksums |
a85f89b54b59ed13b96682a8038515946772091aa01b82ca190da28d71ec1856
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.12 {"installer":{"name":"uv","version":"0.12.12","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|