A command-line tool for measuring code metrics with tree-sitter. It scans a project and reports high-risk files, functions, and React components so you can spot code that is worth refactoring. A programmatic API is also available.
# Run without installing
npx code-gauge path/to/project
# Or install globally
npm install -g code-gauge
code-gauge path/to/projectThe CLI scans JavaScript, JSX, TypeScript, TSX, Python, Go, Rust, Java, Ruby, C, and C++ files. By default it skips generated, vendor, test, and tool directories, prints a summary, and lists the highest-risk findings. TypeScript project metrics and React component classification turn on automatically when a tsconfig.json is found.
| Option | Description |
|---|---|
--config <path> |
Use this config file instead of the auto-detected code-gauge.config.json. |
--include-tests |
Include test files and test directories. |
--tsconfig <path> |
Use this tsconfig.json instead of the auto-detected one. |
--max-findings <n> |
Maximum number of findings to print (default: 20). |
--largest-files <n> |
List the n largest files by code LOC (config key: largestFiles). |
--json |
Print machine-readable JSON. |
--fail-on-risk |
Exit with code 1 when any high-risk finding is reported. |
--fail-on-error |
Exit with code 1 when any file or directory cannot be scanned. |
--<metric>-threshold <n> |
Override a risk threshold (see below). |
A finding is reported when a measured value is greater than or equal to its threshold. Every threshold can be set on the command line (e.g. --file-loc-threshold 400) or in a config file; the command line wins over the config file, which wins over the defaults.
code-gauge looks for code-gauge.config.json by walking up from the target directory (override with --config). The following config reproduces every built-in default:
{
"thresholds": {
"fileLoc": 500,
"functionLoc": 120,
"componentLoc": 350,
"cognitive": 25,
"cyclomatic": 20,
"call": 50,
"import": 25,
"fanOut": 10,
"parameter": 8,
"duplicateBlock": 2,
"duplicationRatioPercent": 30,
"transitiveDependency": 25,
"structuralBreadth": 8,
"structuralCoordination": 300,
"stateMutation": 50,
"duplicateSymbolGroup": 5
},
"languageThresholds": {
"python": { "stateMutation": 90, "structuralCoordination": 350 },
"ruby": { "stateMutation": 90, "structuralCoordination": 350 },
"react": { "import": 30 }
},
"maxFindings": 20,
"includeTests": false,
"failOnRisk": false,
"failOnError": false
}| Threshold | CLI flag | Reports when a … |
|---|---|---|
fileLoc |
--file-loc-threshold |
file's code LOC is large. |
functionLoc |
--function-loc-threshold |
function's physical LOC span is large. |
componentLoc |
--component-loc-threshold |
React component's physical LOC span is large. |
cognitive |
--cognitive-threshold |
function's cognitive complexity is high. |
cyclomatic |
--cyclomatic-threshold |
function's cyclomatic complexity is high. |
call |
--call-threshold |
function makes many calls. |
import |
--import-threshold |
file has many unique import sources. |
fanOut |
--fan-out-threshold |
function calls many other in-file functions. |
parameter |
--parameter-threshold |
function declares many parameters. |
duplicateBlock |
--duplicate-block-threshold |
file contains copy-pasted code blocks. |
duplicationRatioPercent |
--duplication-ratio-percent-threshold |
large percentage of a file's code lines is duplicated. |
transitiveDependency |
--transitive-dependency-threshold |
file transitively reaches many local files. |
structuralBreadth |
--structural-breadth-threshold |
file coordinates many structural concerns. |
structuralCoordination |
--structural-coordination-threshold |
file's structural coordination score is high. |
stateMutation |
--state-mutation-threshold |
file mutates state heavily. |
duplicateSymbolGroup |
--duplicate-symbol-group-threshold |
file shares many duplicated symbols with others. |
Some metrics distribute very differently by language or file type, so a single global threshold either
over-flags one language or under-flags another. languageThresholds overrides individual thresholds for a
profile without repeating the whole set. Each file resolves its thresholds as base → its language profile →
the react profile (the last applies when the file contains a React component), so later profiles win.
Valid profile keys are javascript, jsx, typescript, tsx, python, go, rust, java, ruby, c,
cpp, and react. Built-in
overrides raise stateMutation and structuralCoordination for Python and Ruby (every binding is an
assignment, so these run far higher than in TypeScript) and raise import for React files (which pull in
many components).
Anything you specify is merged on top of the built-in overrides, so { "python": { "stateMutation": 8 } }
restores the global value for Python while keeping the other built-in adjustments.
{
"languageThresholds": {
"python": { "stateMutation": 120 },
"react": { "componentLoc": 400, "import": 35 }
}
}Command-line --<metric>-threshold flags set the global base only; use the config file for per-language tuning.
- Physical LOC, code lines, comment-only lines, and blank lines
- Function and class counts
- Cyclomatic and cognitive complexity (per function and maximum)
- Nesting depth
- Intra-file call graph metrics: call counts, fan-in/fan-out, recursion, call depth, and parameter counts
- Within-file duplication: copy-pasted blocks and statement runs matched on normalized tokens (identifiers anonymized consistently, literals by kind), plus duplicated line count and ratio (distinct from cross-file duplicate symbol names)
- File coupling (imports/exports) and cohesion (shared function identifiers)
- Architecture metrics: transitive local dependencies, structural coordination and breadth, state mutation, and cross-file duplicate symbols
- TypeScript type-shape metrics: annotations, aliases, interfaces, generics, unions, intersections, assertions, and conditional types
- Halstead metrics and the maintainability index
Built-in parsers cover JavaScript, JSX, TypeScript, TSX, Python, Go, Rust, Java, Ruby, C, and C++. Additional tree-sitter grammars can be registered with TreeMeasurer.registerLanguage.
Measurement is also implemented as a Rust addon that produces bit-identical metrics roughly 13x faster than the TypeScript backend (the tree-sitter grammar crates are pinned to the same versions as the npm grammar packages). With a Rust toolchain installed, build it once:
yarn build-nativemeasureCode and the CLI pick up native/code-gauge.node automatically and fall back to the TypeScript implementation when the addon is missing (for example, on npm installs) or when a custom language has been registered. Set CODE_GAUGE_NATIVE=0 to force the TypeScript backend, and compare both with yarn benchmark (requires yarn build first). isNativeBackendAvailable() reports which backend is in use.
import { measureCode } from 'code-gauge';
const metrics = measureCode(
`
function score(value) {
if (value < 0 || value == null) {
return 0;
}
return value > 10 ? 10 : value;
}
`,
{ language: 'javascript' }
);
console.log(metrics.cyclomaticComplexity);