Skip to main content
mehen top-offenders walks one or more paths, computes head-only metrics, and emits a deterministic sorted list of the worst-offending files. It is the right tool for repository health dashboards, prioritization reports, and “where do we spend time?” planning.

File discovery

Directory walks respect .gitignore, .ignore, .git/info/exclude, parent ignore files, and the user’s global Git excludes by default. Ignored directories are pruned before their files are scheduled for analysis. This keeps generated and vendored trees such as node_modules, target, dist, and cdk.out out of the parser workload when the repository ignores them. Within Git repositories, mehen also reads the normal Git attribute hierarchy, including nested .gitattributes, .git/info/attributes, and configured global attributes. Nested repositories use their own hierarchy. Files with any of these attributes set, or assigned the value true, are excluded before parser dispatch:
  • linguist-generated
  • linguist-vendored
  • binary
An attribute explicitly unset or assigned false does not exclude the file. Pass --no-ignore to disable both ignore-file and Git-attribute filtering. Hidden children are still skipped. An explicit file path is always considered, even when an ignore rule or attribute matches it, so a deliberately targeted file can still be measured.

Known metric names

cyclomatic, cognitive, nom.functions, loc.lloc, mi.original, mi.sei, mi.visual_studio, halstead.volume, abc, plus every other key the analyzers publish onto a file’s root metric set — the full LOC family (loc.sloc, loc.ploc, …), aggregate spellings (cognitive.max, loc.lloc.avg, nom.functions.max), nargs, nexit, npa.*, npm.*, wmc, the git history family (history.churn.abs, history.churn.relative, history.age_months, history.authors, history.minor_contributors, history.ownership, history.commit_frequency, history.hotspot, history.sum_of_coupling, history.twr, history.bugfix_commits — these require a full, non-shallow git clone (e.g. a CI checkout with fetch-depth: 0) and walk the history of HEAD), the test-coverage family (coverage.line, coverage.branch, coverage.function and their .covered/.total counters — these ingest coverage reports and rank unmeasured files as n/a, never as 0%), plus the full Markdown family from Markdown metrics. These are the same names a mehen.toml threshold accepts.

Polarity

Each metric has a default direction. mehen knows that higher cognitive complexity is worse, higher maintainability index is better, etc. Override with the prefix:
  • --metric +mi.visual_studio — rank highest MI first (best at top).
  • --metric -cognitive — rank highest cognitive first (worst at top).
  • --metric cognitive — same as -cognitive because cognitive’s default polarity is “lower is better”.

Examples

Determinism

The output ordering is fully deterministic: same visible paths, ignore configuration, metrics, and head ref → byte-identical report. Tie-breaking falls back to the next --metric, then to the repository-relative path. There is no sampling and no file-system-order dependence.

Configured thresholds

When a mehen.toml is present, the configured limits for the selected --metric names are checked against every analyzed file — not just the displayed top N, so a violation cannot hide below --max-results. The ranking still prints in full; crossed limits then print a grouped report on stderr and the command exits 1.

Exit codes

See also