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-generatedlinguist-vendoredbinary
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-cognitivebecause cognitive’s default polarity is “lower is better”.
Examples
- Worst by cognitive
- Multi-key sort
- Filter to one tree
- Markdown docs only
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 amehen.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
mehen metrics— single-file analysis.mehen diff— compare two revisions.- Code metrics, Markdown metrics — the metric catalog you can rank by.