Churn — how often it changes
gitmeta scans the working tree once and returns per-file commit counts, last author and last-changed time — no git log per file.
polyglot · 17 languages · churn × complexity × coupling
Complex code that never changes is dormant. Churny code that's trivial is cheap. The code that hurts is both — high complexity and high churn. hotspot multiplies the two into one ranked list, in any language, so you fix what actually costs you.
↑ Click a language — 17 supported, one ranked score. MIT · single static binary, no cgo · composes gitmeta + codemetrics + go-coupling + treesitter-symbols
how it works
hotspot doesn't re-implement git or a parser — it fuses libraries that each do one thing well, normalizes every signal to 0–1 (log-scaled, so one giant file can't flatten the rest), multiplies churn by complexity, then lets coupling amplify.
gitmeta scans the working tree once and returns per-file commit counts, last author and last-changed time — no git log per file.
codemetrics computes per-function cyclomatic and cognitive complexity across 17 languages — Go via go/ast, the rest via a pure-Go tree-sitter backend. hotspot sums them per file (cognitive preferred — it tracks human effort).
treesitter-symbols extracts each file's imports (pure Go, 17 languages); go-coupling turns them into a first-party import graph and returns each node's afferent/efferent coupling (Ca/Ce) — for Go, Rust, Python, JS/TS, Ruby, C/C++, Swift, and the declaration-based Java, Kotlin, Scala, C#, PHP, Perl. Entangled code amplifies the risk score — it's costlier to change safely.
// base = norm(churn) × norm(complexity) // coupling amplifies; renormalized so top = 1.0 score := base * (1 + couplingNorm)
# library, not just a CLI rep, _ := hotspot.Analyze(ctx, ".", hotspot.Options{}) for _, f := range rep.Files[:10] { fmt.Println(f.Score, f.Path) }
what you get
Every file gets a 0–1 risk score and a stable ranking — triage the top of the list instead of drowning in per-rule warnings.
Point it at a repo. It walks tracked source, skips vendor/build dirs, and degrades to a pure complexity ranking outside a git tree.
A readable table for humans, structured JSON (score, norms, author, commits) for dashboards, bots, and CI gates.
A single static binary. The Go complexity path uses go/ast; churn shells out to your git. Nothing to install but the tool.
hotspot.Analyze returns the full report; the scorer is pure and exported. Signals sit behind small interfaces, so new dimensions drop in cleanly.
Each score shows its parts — commits, cognitive, cyclomatic, functions — so a hotspot is a conversation starter, not a black box.
install
brew install --cask richardwooding/tap/hotspot
go install github.com/richardwooding/hotspot/cmd/hotspot@latest
hotspot -top 20 /path/to/repo
import "github.com/richardwooding/hotspot"
Needs a git binary on PATH for churn. Complexity spans 17 languages and coupling spans 13 ecosystems — a single static binary, no cgo. Or grab a prebuilt binary for macOS, Linux or Windows from the releases page.
roadmap
Each dimension already has a home in the portfolio and slots in behind the existing interfaces — the score just gets sharper.