Skip to content
mbackschatPublic

About

Measure lines of code (LOC) by language, folder and purpose; explain classification and compare saved reports across projects and workspaces.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

LOCmetry logo

LOCmetry

CI CodeQL Latest release Licence: MIT

Line counters such as Tokei, cloc and scc report how many lines of each language a repository holds. LOCmetry reports what those lines are for: it counts with its own fork of Tokei, then classifies every file as production, test or other code and by category, such as generated code or tooling, keeps data and documentation out of source totals, and records the rule behind each classification so you can check it.

Line counters (Tokei, cloc, scc) LOCmetry
Code, comments and blanks per language Yes Yes, counted by a Tokei fork that reads strings and comments as each language's own lexer does
Production, test and other code Not built in; scc can map file patterns to custom categories Built in from build modules and test conventions, refined by a reviewed policy
Data and documentation Counted next to source: JSON counts as code Reported apart from source code
Why a file counts where it does No classification to explain locmetry explain PATH names the deciding rule
Change between two measurements cloc --diff compares source trees locmetry compare compares saved reports by purpose, category, language and folder

The comparison on Immich shows what that changes: the counters count its database schemas, OpenAPI specification and translations as code.

LOCmetry measures Immich by language and purpose, adds its folder and category tables, explains why a generated SDK client counts as other code, and compares two Immich releases

It needs no configuration to start: LOCmetry recognizes the build modules of npm, Gradle, Maven, Cargo, Go, Python, Lake, Swift, Dart, .NET, Composer and RubyGems projects and of Next.js, Nuxt, React Router, Symfony, Laravel, Rails and Django applications, counts their conventional source layouts as production and finds tests by built-in conventions, so even a multi-language monorepo gets a production/test split on the first run.

Install

With Homebrew on macOS arm64 or Linux x86_64 and aarch64:

brew install mbackschat/tap/locmetry

Or download a binary from the latest release; releases after 0.7.6 also include a zip for Windows x86_64. LOCmetry is a self-contained executable with no separately installed runtime or counter. Linux binaries are static, the Windows binary needs no Visual C++ runtime, and macOS uses operating-system libraries. Releases after 0.9.0 also include .deb and .rpm packages for Debian, Ubuntu, Fedora and other Linux distributions. See manual installation, Linux packages and source builds for alternatives.

Homebrew and the Linux packages also install the locmetry(1) man page and bash, zsh and fish completions, and release archives include them. For other installations, locmetry --guide reference --section man-page-and-completions shows how to generate and install them.

Windows and PowerShell

Install with Scoop:

scoop bucket add mbackschat https://github.com/mbackschat/scoop-bucket
scoop install mbackschat/locmetry

Run LOCmetry from PowerShell; the common commands work there as written, including globs in single quotes such as --exclude 'vendor/**'.

  • Save reports with --output FILE rather than >. Windows PowerShell 5.1 redirects as UTF-16; locmetry compare reads that, but other tools may not.
  • Personal defaults live in %APPDATA%\locmetry\config.toml.
  • For command and option completion, add locmetry --generate complete-powershell | Out-String | Invoke-Expression to the file $PROFILE names.
  • The optional compare-locmetry-files.sh helper is a Bash script and needs Bash, for example from Git for Windows, and jq (winget install jqlang.jq).

What you see

These excerpts measure Immich v3.3.4, a popular self-hosted photo platform with a TypeScript server, a Svelte web client, a Flutter app and a Python machine-learning service. LOCmetry measures it with a short reviewed policy kept in this repository; the refresh script regenerates every excerpt from the pinned checkout.

New to the vocabulary? Terms explains purpose, category, unclaimed and the other words in these excerpts.

Take the tour to build that policy step by step, from the first measurement to reviewed decisions.

▶️ The showcase recordings replay measurement, workspace and comparison on Immich; the command guides cover every option on a small fixture.

LOCmetry answers "how much source, and how much of it is tests"

cd immich && locmetry
 ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
 Language    Files    Lines     Code │ Comments  Blanks │   Prod.    Test   Other │ Test/prod │  Share  Code share
 ────────────────────────────────────┼──────────────────┼─────────────────────────┼───────────┼─────────────────────────
 TypeScript  1,085  171,956  145,881 │    4,679  21,396 │  70,799  68,616   6,466 │      0.97 │  51.6%  ████████▓▓▓▓▓▓▓░
 Dart          823   90,871   75,726 │    2,779  12,366 │  59,495  16,231       0 │      0.27 │  26.8%  ██████▓▓
 JavaScript     17   23,272   19,744 │      428   3,100 │  19,682      62       0 │      0.00 │   7.0%  ██
 Svelte        412   19,014   17,440 │      202   1,372 │  17,418      22       0 │      0.00 │   6.2%  ██
 SQL            37   10,534    9,699 │      436     399 │       0       0   9,699 │         - │   3.4%  ░
 Python         39    6,184    4,888 │       79   1,217 │   2,818   2,070       0 │      0.73 │   1.7%  █
 Kotlin         35    4,604    3,810 │      177     617 │   3,810       0       0 │      0.00 │   1.3%  ▏
 Swift          28    3,874    3,103 │      232     539 │   3,103       0       0 │      0.00 │   1.1%  ▏
 CSS             2      818      691 │       26     101 │     691       0       0 │      0.00 │   0.2%  ▏
 Shell          19      565      404 │       69      92 │     404       0       0 │      0.00 │   0.1%  ▏
 Dockerfile      4      369      267 │       12      90 │     257      10       0 │      0.04 │   0.1%  ▏
 C++             1      295      251 │       11      33 │     251       0       0 │      0.00 │   0.1%  ▏
 HCL            16      262      225 │        4      33 │     225       0       0 │      0.00 │   0.1%  ▏
 C               2      225      168 │       30      27 │     168       0       0 │      0.00 │   0.1%  ▏
 Groovy          3      173      139 │        7      27 │     139       0       0 │      0.00 │   0.0%  ▏
 Java            1      166      124 │       25      17 │     124       0       0 │      0.00 │   0.0%  ▏
 HTML            1       74       66 │        2       6 │      66       0       0 │      0.00 │   0.0%  ▏
 PostCSS         0       31       28 │        0       3 │      28       0       0 │      0.00 │   0.0%  ▏
 CMake           1       13        9 │        0       4 │       9       0       0 │      0.00 │   0.0%  ▏
 C Header        1        1        1 │        0       0 │       1       0       0 │      0.00 │   0.0%  ▏
 ────────────────────────────────────┼──────────────────┼─────────────────────────┼───────────┼─────────────────────────
 Total       2,527  333,301  282,664 │    9,198  41,439 │ 179,488  87,011  16,165 │      0.48 │ 100.0%

 Lines = Code + Comments + Blanks · Production + Test + Other = Code · Test/prod = Test / Production (Code)
 Tests written in another language than the code they test skew per-language Test/prod.
 Folded: BASH → Shell, TSX → TypeScript.
 Rows with 0 files count code embedded in files of other languages, such as scripts in HTML: PostCSS.
 Share = Code / total Code. Bars: █ Production  ▓ Test  ░ Other. The largest row fills the bar.

Code is split by purpose; comments and blanks stay separate. Data and documentation do not inflate source totals. Language variants count within their family's row, here TSX within TypeScript, and the Folded: line names each fold; --split-languages shows them separately, and JSON keeps every backend language. Test/prod divides test code by production code; it describes volume, not coverage. If your numbers differ from these, personal defaults in ~/.config/locmetry/config.toml (on Windows %APPDATA%\locmetry\config.toml) may apply; --no-user-config ignores them.

Compared with a line counter: "how much text, per language"

Line counters are good at counting, and LOCmetry counts with Tokei too. Their totals for the same checkout run to almost twice the code above, because they count data as code: about 40% is JSON alone, such as database schemas, the OpenAPI specification and translations.

Tool Reported code of which JSON Production / test split
Tokei 15.0.0 518,920 209,607 (40%) none
cloc 2.10 530,430 209,931 (40%) none
scc 4.1.0 510,075 209,867 (41%) none

A reviewed policy answers "what does this project consist of"

Built-in conventions already recognize the npm, Gradle, Python and Dart build modules and the conventional unit, integration and end-to-end tests. A policy adds what only the project knows, with a recorded reason for each decision: generated code, end-to-end support code, the iOS app, deployment files and developer tooling.

Source code Built-in conventions Reviewed policy
Production 194,167 179,488
Test 83,595 87,011
Other: generated 0 16,165
Other: unclaimed 4,908 0
Test/prod 0.43 0.48

--show-categories breaks each purpose down by category, which shows what the project consists of:

cd immich && locmetry --show-categories
 ────────────────────────────────────────────────────────────────────────────────────────────────────────
 Category                       Files    Lines     Code │ Share  Code share
 ───────────────────────────────────────────────────────┼────────────────────────────────────────────────
 production:implementation      1,828  198,193  169,311 │ 59.9%  ████████████████████████████████████████
 test:test                        450   87,800   73,710 │ 26.1%  ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
 other:generated                   38   19,173   16,165 │  5.7%  ░░░░
 test:end-to-end                   41    9,285    7,871 │  2.8%  ▓▓
 production:native                 67    8,870    7,206 │  2.5%  ██
 test:test-support                 27    3,970    3,352 │  1.2%  ▓
 test:integration                   8    2,296    2,014 │  0.7%  ▏
 production:build-tooling          18      886      772 │  0.3%  ▏
 production:deployment             28      938      743 │  0.3%  ▏
 production:distribution            5      695      567 │  0.2%  ▏
 production:tooling                10      646      507 │  0.2%  ▏
 production:documentation-site      6      468      382 │  0.1%  ▏
 test:performance                   1       81       64 │  0.0%  ▏

 Source files only. Rows of one purpose add up to that purpose's Code, and all rows add up to the total Code.
 Share = Code / total Code. Bars: █ Production  ▓ Test  ░ Other. The largest row fills the bar.

Categories are labels assigned by built-in conventions, such as test and build-tooling, and by the policy, such as generated, native and tooling; a line counter has no equivalent. The showcase narrows the tables to the code around the product with --table-category and focuses on the mobile app with --subfolder.

locmetry explain FILE names the rule behind any decision; for the generated SDK client, it reports the rule and the reason recorded with it.

Workspaces and comparisons answer "where, and what changed"

Measure the application components together under the same policy. The workspace file names them as folders of the project:

version = 1
# The application components of Immich, each measured under the checkout's
# .locmetryrc as --subfolder would; root-only folders such as docs/ and
# deployment/ stay in the root measurement.
project = "."
folders = ["server", "web", "mobile", "machine-learning", "e2e", "packages"]
locmetry workspace --workspace-file immich/workspace.toml
 ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
 Member              Lines     Code │   Prod.    Test   Other │ Test/prod │  Share  Code share
 ───────────────────────────────────┼─────────────────────────┼───────────┼─────────────────────────────────────────────
 server            129,296  111,899 │  53,245  48,955   9,699 │      0.92 │  39.7%  █████████████████▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░░░
 mobile            100,095   83,183 │  66,952  16,231       0 │      0.24 │  29.5%  ██████████████████████▓▓▓▓▓
 web                67,832   58,252 │  52,864   5,388       0 │      0.10 │  20.7%  █████████████████▓▓
 e2e                15,491   13,174 │      61  13,113       0 │    214.97 │   4.7%  ▓▓▓▓
 packages           12,893   10,053 │   2,333   1,254   6,466 │      0.54 │   3.6%  █░░
 machine-learning    6,698    5,291 │   3,221   2,070       0 │      0.64 │   1.9%  █▓
 ───────────────────────────────────┼─────────────────────────┼───────────┼─────────────────────────────────────────────
 Total             332,305  281,852 │ 178,676  87,011  16,165 │      0.49 │ 100.0%

 Production + Test + Other = Code · Test/prod = Test / Production (Code) · Share = Code / workspace Code
 Bars: █ Production  ▓ Test  ░ Other. The largest row fills the bar.

Compare saved reports of two releases, v3.2.0 and v3.3.4:

 ──────────────────────────────────────────────────────────────────
                         Lines           │           Code
 Purpose        before    after   change │  before    after  change
 ────────────────────────────────────────┼─────────────────────────
 Production    207,995  210,696   +2,701 │ 177,399  179,488  +2,089
 Test           96,227  103,432   +7,205 │  81,161   87,011  +5,850
 Other          17,825   19,173   +1,348 │  14,910   16,165  +1,255
 ────────────────────────────────────────┼─────────────────────────
 Source total  322,047  333,301  +11,254 │ 273,470  282,664  +9,194

 Test/prod (Code): 0.46 → 0.48 (before → after)

Most of the change is test code. Folder reporting and comparison explain the remaining views.

Terms

Term Meaning
Content kind Each counted file is source, data such as JSON, or documentation such as Markdown, decided by its language. Tables and totals count source; data and documentation are reported separately.
Purpose production, test or other. Production is maintained non-test source, including build scripts and tooling; other is source that counts as neither, such as generated code a policy sets aside or code nothing claimed.
Category A finer label inside a purpose, written purpose:category, such as production:implementation, production:build-tooling or test:test. It changes no totals; --show-categories breaks tables down by it. locmetry --guide categories lists the reference names and how to choose one.
Unclaimed Source that no convention, module or policy entry classified, reported as other:unclaimed and listed by folder for review. Its lines are counted; only its purpose is open.
Build module A folder with a build or framework file, such as package.json, Cargo.toml or next.config.ts; its conventional source folders are production. See build modules.
Convention A built-in classification, such as files in test/ being tests or vite.config.ts being build tooling. Conventions apply before the project's own rules; built-in detection lists them.
Policy The project's .locmetryrc: scope exclusions and entries that classify what the built-ins cannot. rules apply after conventions, overrides before them, and refinements change only the category of files already decided. See configuration.
Provenance The convention, module or entry that decided a file, recorded as its rule; locmetry explain PATH shows it.
Test/prod Test code divided by production code, with Other left out. It describes volume, not test coverage.
Coverage How many selected files were counted. Unrecognized or unreadable files are reported, never counted as zero.
Workspace, member Several repositories, each under its own policy, or folders of one project under its policy, measured together into one report; each repository or folder is a member.

PRODUCT defines these terms in full.

Common commands

Task Command
Measure another project locmetry --root ../service
Inspect scope, classification and coverage notes locmetry --verbose
Measure a subfolder with project policy and module context locmetry --subfolder web/src
Explain a file's classification or missing counts locmetry explain web/src/orders/Order.test.ts
Save full JSON with per-file decisions outside the root locmetry --files --output ../report.json
Save full JSON and print the table from one scan, as for agents locmetry --output ../report.json --print table
Save a Markdown report locmetry --format markdown --output ../report.md
Measure repositories together, each with its own policy locmetry workspace --root ../web --root ../api
Show production source in the table, with its comment density locmetry --table-purpose production
Inspect the configuration contract locmetry --schema config
Read full JSON on stdout locmetry --root service --format json --files
Measure selected source, including ignored files, except vendor locmetry --root service --subfolder src --exclude 'src/vendor/**' --hidden true --respect-ignore-files false --format json
Show adaptive folder detail and production categories locmetry --root service --folder-rows 30 --table-purpose production --show-categories --show-bars=comments --sort name
Save a sorted Markdown tree to depth 3 locmetry --root service --format markdown --markdown-depth 3 --sort name --output report.md
Measure an explicit workspace definition locmetry workspace --workspace-file repos.toml --files --output workspace.json --print table
Compare two saved full reports locmetry compare before.json after.json --format table

locmetry --help includes the same commands, plus a working configuration example for maintained scripts with applicability and verification instructions. Verbose tables total data and documentation files under “Not source code”; to list them, filter full JSON by kind.

Without explicit options, a terminal gets a table and a pipe gets a compact JSON headline. An option of one format, such as --show-categories for tables, selects that format in a pipe too. --output defaults to full JSON; choose --format table or --format markdown explicitly for those exports. File exports are silent on stdout, and the extension does not select the format. For tables alone, add --tables-only: it drops legends, notes and the closing location and date lines, as for transcripts and scripts that need only rows. Output routing and reports explains the choices and protected destinations.

Selected options

Selection and policy — choose the measured input and its rules.

Option Use
--root DIR / --subfolder DIR Select a project root, or restrict measurement within it while retaining project policy and module context.
--workspace-file FILE Select a workspace definition for workspace or explain.
--config FILE / --exclude GLOB Choose project configuration or add a root-relative scope exclusion.
--no-user-config Ignore personal defaults, for example in CI.

Output and content — choose formats, destinations and JSON detail.

Option Use
--format FORMAT / --output FILE Choose the result format and destination; --output - selects stdout.
--print FORMAT With a saved file, also print another view from the same scan.
--files Include per-file counts and decisions in full JSON, whether saved or printed; headlines omit them.

Presentation — shape views without changing measured scope.

Option Use
--show-folders / --folder-rows N Top-level / adaptive folder rows in a single-root table, saved or printed.
--show-categories / --verbose Add category rows / classification and coverage notes to tables.
--table-purpose LIST Filter table source and totals by purpose; JSON and Markdown retain the complete measurement.
--table-category LIST Filter table source and totals by category, such as test,production:tooling or 'production:*tooling*'; a note names the excluded categories and their code.
--table-language LIST Filter table source and totals by language, such as TypeScript,Svelte; a note names the excluded languages and their code.
--split-languages Show language variants such as TSX, JSX and C++ Header as their own rows instead of within TypeScript, JavaScript and C++.
--show-bars[=KIND] / --no-bars Force / suppress table charts. KIND is purpose or comments; selecting one table purpose uses comment density even with =purpose. Terminals show fitting bars automatically.
--tables-only Print only the tables of a root, workspace or compare table report, without legends, notes or the closing location and date lines.
--markdown-depth N / --sort code|name Bound Markdown Folder breakdown depth / order human rows; depth does not bound other evidence.
--no-color Disable terminal color, bold and dim, including help and diagnostics; equivalent to NO_COLOR=1.

Use the current spellings above; the option contract lists rejected former names and format-specific warning/error behavior.

Run locmetry --help or locmetry COMMAND --help for all options. The positional directory is equivalent to --root; in scripts, prefer --root "$directory" so a directory named compare or workspace cannot select a command. Command options follow the command name; global --no-color works before or after it.

For agents

One call gives an agent both a readable overview and a saved report:

locmetry --root . --output ../report.json --print table

Stdout gets the table, unstyled and without share bars in a pipe; the file gets full JSON for queries or a later locmetry compare. Use --print headline for compact JSON on stdout instead, and locmetry --schema report for every field. --show-folders and --verbose extend the printed table.

Compare saved measurements

Save full baselines outside the measured root; a headline cannot reconstruct a baseline. Net change is after minus before:

locmetry --root ./service --output ./before.json
# After changing the source or its measurement policy:
locmetry --root ./service --output ./after.json
locmetry compare before.json after.json

For two saved workspace reports, drill into one root by its exact member name:

locmetry compare before-workspace.json after-workspace.json --member service --format table

Check Member: service and its Source total; the folder table shows changes in immediate subfolders. The name must occur in either report. Totals and detail cover that member; input metadata and partial status still cover the whole workspace inputs. See workspace capture and member selection for baseline commands and JSON scope.

The comparison guide covers changed scope, producer versions, partial inputs and workspace changes. Reports describe measured working copies, not Git commits. For automated policies, use the CI recipes, which preserve measurement failure and partial-result status before querying JSON, and the file-size gates, which limit source files by purpose, with soft limits and frozen sizes for existing large files.

For file-level recognition and classification changes, run compare-locmetry-files.sh before.json after.json. This optional Bash helper accepts two full --files reports and ships in release archives and Homebrew installations starting with 0.7.3. Only the helper requires jq; install it separately if needed. LOCmetry's native commands remain independent of jq. See the transition workflow for setup, output and evidence limits.

How classification works

LOCmetry embeds a fork of Tokei 15.0.0 for counting and applies project policy to each file. Tokei misreads literals it does not know, such as '"', raw strings or interpolation holes, and then counts the comments after them as code; the fork reads them as each language's own lexer or parser does, checked on public repositories, and decides by content the extensions several languages share. Tokei defects lists what it corrects. Purpose, category and content kind are separate dimensions, as Terms explains. Kind follows the physical file, so a fenced code block in Markdown or other documentation counts as documentation, never as source; --verbose shows how many code lines such snippets hold. Rust doc comments, doctests included, stay comments of their file.

  • Module context: selected build descriptors, such as npm, Gradle, Cargo, Go, Python or .NET project files, and framework descriptors, such as next.config.ts or Laravel's artisan, identify conventional source layouts, as build modules describes. The innermost module supplies context, so nested projects do not inherit an outer project's layout blindly.
  • Ordered decisions: explicit overrides precede test and build conventions, ordinary project rules, module inference and fallback. Tests inside production source trees remain tests; overrides can express exceptions. Refinements then give decided files a finer category without changing their purpose. Every counted file records the deciding rule, and reports count what each policy entry decided.
  • Scope and coverage: exclusions and ignore rules determine what is selected. Unrecognized files and unavailable counts remain distinguishable from zero. Read/parse failures produce a partial report and exit 2; unresolved classification alone does not make counts partial.

These Immich files show each kind of decision, from module layout and test conventions to project entries and refinements:

File Purpose / category Deciding rule
server/src/services/asset.service.ts production / implementation builtin:npm-module
mobile/lib/main.dart production / implementation builtin:dart-module
server/src/services/asset.service.spec.ts test / test builtin:js-test-file
e2e/src/specs/web/album.e2e-spec.ts test / end-to-end builtin:js-e2e-spec-file, refined by end-to-end
e2e/src/utils.ts test / test-support end-to-end-suites (project entry), refined by end-to-end-support
mobile/ios/Runner/AppDelegate.swift production / native ios-app (project entry)
packages/sdk/src/fetch-client.ts other / generated generated-sdk-client (project entry)

Inspect the evidence for one file, inside the Immich checkout:

locmetry explain packages/sdk/src/fetch-client.ts

The explanation identifies the deciding stage, the matching pattern and the reason recorded with a project entry. It also reports parser, counts and module evidence where applicable. The selection guide explains other states and how to investigate a missing file.

Built-in wrapper patterns cover gradlew, gradlew.bat, mvnw, mvnw.cmd, and files under gradle/wrapper/ or .mvn/wrapper/ at any depth. The enabled convention classifies them as other / third-party-wrapper unless an override or earlier convention wins; wrapper details and precedence define the conditions.

Classification applies to whole files, so inline tests cannot receive a separate purpose. When Rust production files contain #[cfg(test)] items, a line below the language table names their code, such as “Production in 'Rust' includes 145 code lines of inline #[cfg(test)] tests in 3 files”, and full JSON reports them as inline_tests. Measurement limits define this and other boundaries.

Build modules

LOCmetry recognizes build modules by their descriptors and counts each one's conventional source layout as production, with no configuration and across any mix of build systems in one tree:

Build system Descriptor Production source
npm package.json src/**
Gradle build.gradle, build.gradle.kts src/main/**, Kotlin Multiplatform src/*Main/**
Maven pom.xml src/main/**
Cargo Cargo.toml src/**
Go go.mod every *.go file outside vendor/, testdata/ and names starting with _ or .
Python pyproject.toml, setup.py, setup.cfg src/** and top-level packages
Lake (Lean) lakefile.lean, lakefile.toml every *.lean file
Swift Package.swift Sources/**
Dart and Flutter pubspec.yaml lib/**, bin/**
Next.js next.config.* app/**, pages/**, src/**, root middleware, instrumentation and similar files, and the folders the docs show, such as components/** and lib/**
Nuxt nuxt.config.* app/**, server/** and the other documented folders, app.vue, error.vue
React Router react-router.config.* app/**
.NET *.csproj, *.fsproj, *.vbproj every *.cs, *.fs, *.vb, *.razor, *.cshtml and *.xaml file, and *.razor.css
Composer composer.json src/**
Symfony symfony.lock src/**, assets/**, config/**, migrations/**, templates/**
Laravel artisan app/**, bootstrap/**, config/**, database/migrations/**, routes/**, resources/** except lang/
Rails config.ru app/**, lib/**, config/**, db/migrate/**
RubyGems *.gemspec lib/**, exe/**
Django manage.py top-level packages

On Immich, cd immich && locmetry --verbose reports what the modules contributed:

 Production from build modules: 107,857 code lines in 8 'npm' modules, 59,170 code lines in 2 'dart' modules, 4,102 code lines in 1 'gradle' module, 3,037 code lines in 1 'python' module.

Tests inside these trees stay tests, and nested modules decide for themselves. Generated code inside these layouts that carries a documented marker, such as Go's // Code generated … DO NOT EDIT. line, Roslyn's *.Designer.cs names and <auto-generated> comments, Java's @Generated on a class or a linguist-generated declaration in .gitattributes, stays production with category generated, so --show-categories separates it from hand-written code. Before module layouts apply, built-in conventions recognize tests, such as test/ folders, *.test.ts, *Test.java, *_test.go, test_*.py and *_spec.rb, and build tooling, such as Gradle, Maven, CMake and Make scripts, build wrappers and the configuration of named JavaScript tools. Detection reads file names only and never runs a build, so custom layouts need configuration. The built-in detection reference explains how detectors, markers and conventions decide and lists every one.

Configuration

No configuration is required to start. Add .locmetryrc when conventions cannot know local intent: which code is vendored, which scripts are maintained tooling, or which experiments should explicitly count as other. LOCmetry does not automatically recognize vendored or generated code.

Three entries of the Immich policy show the shape: a rule that takes generated code from the npm module layout, a rule for code no build descriptor declares, and a refinement that relabels code without changing its purpose. Each restricts itself to source, records its evidence, and says in a comment why it uses its stage:

version = 1

# A rule suffices to take a file from the npm module layout: rules apply before
# build modules, and no test or build convention claims generated sources.
[[rules]]
id = "generated-sdk-client"
match = ["packages/sdk/src/fetch-client.ts"]
kind = ["source"]
purpose = "other"
category = "generated"
reason = "Header: DO NOT MODIFY, generated using oazapfts from the OpenAPI specification"

# A rule claims the iOS code because no Package.swift declares it, so it would
# otherwise stay unclaimed. The Android code is module source and only needs
# the android-app refinement below.
[[rules]]
id = "ios-app"
match = ["mobile/ios/**/*.swift", "mobile/ios/**/*.h"]
kind = ["source"]
purpose = "production"
category = "native"
reason = "Swift code and headers of the iOS runner and its extensions. No Package.swift declares it"

# The Gradle module layout already counts the Android runner as production.
[[refinements]]
id = "android-app"
purpose = "production"
match = ["mobile/android/app/src/main/**"]
kind = ["source"]
category = "native"
reason = "Kotlin, Java and C code of the Android runner, which the Gradle layout labels implementation"

Tests still win over ordinary rules; an override is needed only to change the purpose a convention gives. To give tests or other decided files a finer category, such as unit, use a refinement, which cannot move code between production and test. Choose names from the category reference (locmetry --guide categories) before declaring custom ones, so that policies and agents name the same kind of code alike. Reports list each entry with the files and code it decided and the decisions it replaced as summary.policy_entries, so an entry that misses most of its files or moves production code into tests shows up. Entries without evidence belong in the review list as unclaimed source, not in the policy. A clean review list alone does not establish correct classification.

The configuration guide covers overrides, refinements, except lists, custom categories, language mappings and evidence-based review. Personal defaults can supply exclusions and language mappings across projects; they cannot assign purposes or categories.

LOCmetry's own .locmetryrc is a maintained example: it excludes the synthetic demonstration workspace, distinguishes operational tooling, distribution and verification infrastructure, and splits its tests into unit, integration and test support with refinements, all named from the category reference. The Immich policy is the larger example, and its README explains why each entry uses its stage. Self-measurement design explains the decisions and how to verify them.

For a production-rule change covered by the example in locmetry --help, inspect existing configuration and measure current decisions, adapt the paths and category, then run the displayed verification command. Reading a guide or schema first is optional for that route. For other needs, read only the section that answers them: locmetry --guide index routes each task to its guide sections and schema definitions, such as locmetry --guide configure --section refine-categories. Verify per-file decisions and unresolved review items before concluding. Guides and schemas are available from the installed binary without a network connection.

Guides and reference

Task Guide in the executable Repository copy
Find the section for a task locmetry --guide index Task index
Configure or review classification locmetry --guide configure Configure
Diagnose a file or subfolder locmetry --guide selection Selection and explanations
Read coverage, query reports or apply a CI policy locmetry --guide reports Reports
Capture baselines and interpret net change locmetry --guide compare Compare
Name categories and follow policy conventions locmetry --guide categories Categories
Gate source file sizes by purpose in CI locmetry --guide reports --section gate-file-sizes Reports

License

Licensed under the MIT License. Release archives and the Homebrew installation include THIRD-PARTY-NOTICES.md with the licences of bundled libraries.

About

Measure lines of code (LOC) by language, folder and purpose; explain classification and compare saved reports across projects and workspaces.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages