code-gauge 5.0.0 → 6.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/README.md +204 -72
  2. package/THIRD-PARTY-NOTICES.txt +1152 -798
  3. package/dist/checkCommand.cjs +5 -0
  4. package/dist/checkCommand.cjs.map +1 -0
  5. package/dist/checkCommand.d.ts +12 -0
  6. package/dist/checkCommand.js +5 -0
  7. package/dist/checkCommand.js.map +1 -0
  8. package/dist/cli.cjs +3 -3
  9. package/dist/cli.cjs.map +1 -1
  10. package/dist/cli.js +3 -3
  11. package/dist/cli.js.map +1 -1
  12. package/dist/cliConfig.cjs +1 -1
  13. package/dist/cliConfig.cjs.map +1 -1
  14. package/dist/cliConfig.d.ts +3 -11
  15. package/dist/cliConfig.js +1 -1
  16. package/dist/cliConfig.js.map +1 -1
  17. package/dist/duplication.cjs +1 -1
  18. package/dist/duplication.cjs.map +1 -1
  19. package/dist/duplication.js +1 -1
  20. package/dist/duplication.js.map +1 -1
  21. package/dist/exclusion.cjs +1 -1
  22. package/dist/exclusion.cjs.map +1 -1
  23. package/dist/exclusion.d.ts +2 -2
  24. package/dist/exclusion.js +1 -1
  25. package/dist/exclusion.js.map +1 -1
  26. package/dist/git.cjs +1 -1
  27. package/dist/git.cjs.map +1 -1
  28. package/dist/git.d.ts +13 -12
  29. package/dist/git.js +1 -1
  30. package/dist/git.js.map +1 -1
  31. package/dist/index.cjs +1 -1
  32. package/dist/index.d.ts +0 -4
  33. package/dist/index.js +1 -1
  34. package/dist/metrics.cjs +1 -1
  35. package/dist/metrics.cjs.map +1 -1
  36. package/dist/metrics.d.ts +1 -2
  37. package/dist/metrics.js +1 -1
  38. package/dist/metrics.js.map +1 -1
  39. package/dist/nativeMetrics.cjs +3 -3
  40. package/dist/nativeMetrics.cjs.map +1 -1
  41. package/dist/nativeMetrics.js +3 -3
  42. package/dist/nativeMetrics.js.map +1 -1
  43. package/dist/scan.cjs +1 -1
  44. package/dist/scan.cjs.map +1 -1
  45. package/dist/scan.d.ts +10 -26
  46. package/dist/scan.js +1 -1
  47. package/dist/scan.js.map +1 -1
  48. package/dist/thresholdCheck.cjs +2 -0
  49. package/dist/thresholdCheck.cjs.map +1 -0
  50. package/dist/thresholdCheck.d.ts +36 -0
  51. package/dist/thresholdCheck.js +2 -0
  52. package/dist/thresholdCheck.js.map +1 -0
  53. package/dist/thresholds.cjs +2 -0
  54. package/dist/thresholds.cjs.map +1 -0
  55. package/dist/thresholds.d.ts +38 -0
  56. package/dist/thresholds.js +2 -0
  57. package/dist/thresholds.js.map +1 -0
  58. package/dist/types.d.ts +8 -4
  59. package/dist/worker.cjs +1 -1
  60. package/dist/worker.cjs.map +1 -1
  61. package/dist/worker.js +1 -1
  62. package/dist/worker.js.map +1 -1
  63. package/native/Cargo.lock +73 -73
  64. package/native/Cargo.toml +16 -21
  65. package/native/code-gauge.wasm +0 -0
  66. package/native/src/dep_degree.rs +6 -6
  67. package/native/src/duplication.rs +38 -12
  68. package/native/src/functions.rs +10 -7
  69. package/native/src/languages.rs +14 -13
  70. package/native/src/measure.rs +16 -8
  71. package/native/src/tree_index.rs +4 -4
  72. package/native/src/util.rs +9 -2
  73. package/package.json +10 -9
  74. package/dist/diffCommand.cjs +0 -5
  75. package/dist/diffCommand.cjs.map +0 -1
  76. package/dist/diffCommand.d.ts +0 -17
  77. package/dist/diffCommand.js +0 -5
  78. package/dist/diffCommand.js.map +0 -1
  79. package/dist/newDuplication.cjs +0 -2
  80. package/dist/newDuplication.cjs.map +0 -1
  81. package/dist/newDuplication.d.ts +0 -40
  82. package/dist/newDuplication.js +0 -2
  83. package/dist/newDuplication.js.map +0 -1
  84. package/dist/regressionGate.cjs +0 -2
  85. package/dist/regressionGate.cjs.map +0 -1
  86. package/dist/regressionGate.d.ts +0 -108
  87. package/dist/regressionGate.js +0 -2
  88. package/dist/regressionGate.js.map +0 -1
package/README.md CHANGED
@@ -7,10 +7,11 @@
7
7
  [![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release)
8
8
  [![wbfy](https://img.shields.io/badge/wbfy-20.23.1-1e90ff.svg)](https://github.com/WillBooster/shared/tree/main/packages/wbfy)
9
9
 
10
- A command-line tool that ranks the files of a project by refactoring priority and gates changes
11
- against metric regressions, built for AI-agent workflows: an agent asked to "refactor this
12
- repository" runs `code-gauge` and starts from the top of the list, and a PR pipeline runs
13
- `code-gauge diff --base main` to force the agent to self-correct a change that made the code worse.
10
+ A command-line tool that ranks the files of a project by refactoring priority and checks code
11
+ against metric thresholds, built for AI-agent workflows: an agent asked to "refactor this
12
+ repository" runs `code-gauge` and starts from the top of the list, `code-gauge check` lists every
13
+ function, file, and duplicated block over a threshold, and a PR pipeline runs
14
+ `code-gauge check --base main` to make the agent fix the violations in the code its change touches.
14
15
  Measurement uses tree-sitter, and the output is deliberately small — only the metrics that tell an
15
16
  agent _what to change_ are measured and reported, so nothing in the output anchors an agent toward
16
17
  out-of-scope "improvements". A [programmatic API](#programmatic-api) is also available.
@@ -50,13 +51,16 @@ over three dimensions:
50
51
  - **File NCSS** — non-commenting source statements, a comment- and formatting-independent size
51
52
  measure calibrated against PMD's `NcssCount`.
52
53
 
53
- Ranking is relative to the scanned project, so no absolute thresholds are involved: the top of the
54
- list is worth refactoring first regardless of where any cutoff would sit. Each reported file carries
54
+ Ranking is relative to the scanned project and ignores the
55
+ [thresholds](#threshold-check-code-gauge-check): the top of the list is worth refactoring first
56
+ regardless of where any cutoff would sit. Each reported file carries
55
57
  the concrete evidence (worst function with location, duplication with partner files, file size) so
56
58
  an agent can act on it directly.
57
59
 
58
60
  ## Options
59
61
 
62
+ The ranking command (`code-gauge [target]`, or spelled out, `code-gauge rank [target]`) accepts:
63
+
60
64
  | Option | Description |
61
65
  | ------------------------------------------ | ------------------------------------------------------------------------------- |
62
66
  | `--config <path>` | Use this config file instead of the auto-detected `code-gauge.config.json`. |
@@ -68,51 +72,162 @@ an agent can act on it directly.
68
72
  | `--duplication-max-gap-tokens <n>` | Maximum token gap merged into one gapped clone group; 0 disables (default 30). |
69
73
  | `--duplication-min-similarity-percent <n>` | Minimum similarity percent for near-miss clones; 100 = exact only (default 70). |
70
74
 
71
- ## Regression gate (`code-gauge diff`)
75
+ ## Threshold check (`code-gauge check`)
72
76
 
73
77
  ```sh
74
- code-gauge diff --base main # gate the working tree against the merge-base with main
75
- code-gauge diff --base main src/api # gate only the changed files under src/api
78
+ code-gauge check # every violation in the current directory
79
+ code-gauge check --base main # only violations in what the working tree changed since main
80
+ code-gauge check --base main src/api # the same, for the changed files under src/api
81
+ ```
82
+
83
+ `code-gauge check [target]` reports every violation of the thresholds below in the files under
84
+ the target. Inside a git repository it measures the repository's git-visible files (tracked, or
85
+ untracked and not ignored), so a block copied from a file outside the target is reported with that
86
+ file as its partner, and a copy that exists only in a git-ignored file is not duplication. The
87
+ built-in skipped directory names (`vendor`, `fixtures`, test directories, ...) apply below the
88
+ target, as in the ranking command, so `code-gauge check vendor` checks what is in `vendor`. A
89
+ git-ignored file is checked only when it is the target itself, and a directory git ignores (itself or
90
+ through a parent directory) is an error as the target (exit code 2). A directory git does not
91
+ ignore passes as a check of zero files when it holds no git-visible source file, for example when
92
+ its only sources are ignored build output. Outside a git repository it walks the target directory
93
+ like the ranking command. The thresholds:
94
+
95
+ | Config key | Violation | Default |
96
+ | --------------------------------- | ------------------------------------------------- | ------- |
97
+ | `maxFunctionCognitiveComplexity` | a function's cognitive complexity is above it | 15 |
98
+ | `maxFunctionCyclomaticComplexity` | a function's cyclomatic complexity is above it | 10 |
99
+ | `maxFunctionNcss` | a function's NCSS is above it | 60 |
100
+ | `maxFunctionNestingDepth` | a function's nesting depth is above it | 4 |
101
+ | `maxFunctionParameterCount` | a function's parameter count is above it | 7 |
102
+ | `maxFunctionHalsteadVolume` | a function's Halstead volume is above it | 2000 |
103
+ | `maxFunctionHalsteadDifficulty` | a function's Halstead difficulty is above it | 20 |
104
+ | `maxFunctionHalsteadEffort` | a function's Halstead effort is above it | 30000 |
105
+ | `maxFunctionDepDegree` | a function's DepDegree is above it | 50 |
106
+ | `maxFileNcss` | a file's NCSS is above it | 500 |
107
+ | `minDuplicateLines` | a duplicated block spans at least this many lines | 10 |
108
+
109
+ A duplicated block is one occurrence of a within-file or cross-file clone, found with the
110
+ [duplication detection settings](#duplication-detection-settings); its span runs from its first to
111
+ its last line. Occurrences of at least `minDuplicateLines` lines that overlap in a file are
112
+ reported as one block covering all of them.
113
+
114
+ Each threshold is set in the [`thresholds` config section](#configuration), where `null` disables
115
+ it and `languages` overrides thresholds for the files of one language, or with the command-line
116
+ option of the same name (`--max-function-ncss 80`, `--min-duplicate-lines off`), where `off`
117
+ disables it. The command line wins over the whole config file, per-language overrides included.
118
+
119
+ With `--base <ref>`, the check covers only what the working tree changed since the merge-base of
120
+ `<ref>` and `HEAD` (committed, staged, unstaged, and untracked changes; nothing is checked out):
121
+
122
+ - a function, when a diff hunk touches its span; deleting lines inside the span counts, and so does
123
+ deleting lines after the last line of a Python function (blank and comment-only lines aside), which may have been its
124
+ tail;
125
+ - a file-level threshold, when the change adds or deletes lines of the file (a rename or a mode
126
+ change alone does not count);
127
+ - a duplicated block, when it overlaps lines the change added.
128
+
129
+ `--base` only filters the report: the files measured and the duplication found are those of the
130
+ check without it, so code pasted from an unchanged file is reported where it was pasted. A
131
+ violation the change did not introduce is reported too once the change touches its function, file,
132
+ or duplicated block. `--base` requires a git repository.
133
+
134
+ When nothing violates a threshold, `check` prints a single line. Otherwise it prints a header with
135
+ the counts, one line per violation ordered by path and line, and one remediation hint per violated
136
+ metric:
137
+
138
+ ```
139
+ Threshold violations: 3 violations (1 functions, 0 files, 2 duplicated blocks) (4 files, 5 functions checked).
140
+ src/legacy.ts:1-21 decide: cognitive complexity 24 (<= 15), cyclomatic complexity 15 (<= 10), nesting depth 5 (<= 4)
141
+ src/report.ts:1-12: duplicated lines 12 (< 10), also at src/summary.ts:1-12
142
+ src/summary.ts:1-12: duplicated lines 12 (< 10), also at src/report.ts:1-12
143
+
144
+ How to fix:
145
+ - cognitive complexity: flatten nested branching with early returns and extract nested blocks into named functions.
146
+ - cyclomatic complexity: the function has too many independent paths; split it by decision or replace condition chains with a lookup table.
147
+ - nesting depth: replace nested conditions with guard clauses or move inner blocks into functions.
148
+ - duplicated lines: extract the repeated code into one shared function or module and call it from every location.
149
+ ```
150
+
151
+ A function gets one line listing every threshold it exceeds, a file-level violation prints the path
152
+ without a line span, and a duplicated block lists up to three of its other copies. Paths are
153
+ relative to the repository root, or to the target directory outside a git repository.
154
+
155
+ | Exit code | Meaning |
156
+ | --------- | -------------------------------------------------------------------------------------------------------------------------------- |
157
+ | 0 | No violations. |
158
+ | 1 | Violations. Whether they are warnings or errors is up to the caller. |
159
+ | 2 | The check is incomplete: a file it covers could not be measured, or the arguments, config file, target, or base ref are invalid. |
160
+
161
+ A file that cannot be measured causes exit code 2 only when the check covers it: a file under the
162
+ target or, with `--base`, a changed file under the target. Other unmeasured files of the
163
+ repository are warnings on stderr.
164
+
165
+ `check` accepts `--config`, `--include-tests`, `--json`, and the `--duplication-*` options of the
166
+ ranking command.
167
+
168
+ ### JSON report
169
+
170
+ `code-gauge check --json` prints:
171
+
172
+ ```json
173
+ {
174
+ "passed": false,
175
+ "base": "main",
176
+ "mergeBase": "9f624517156db3290e189b9fd0b8ed38de39a04c",
177
+ "summary": {
178
+ "violationCount": 2,
179
+ "functionViolationCount": 1,
180
+ "fileViolationCount": 0,
181
+ "duplicationViolationCount": 1,
182
+ "checkedFileCount": 2,
183
+ "checkedFunctionCount": 2
184
+ },
185
+ "violations": [
186
+ {
187
+ "kind": "function",
188
+ "file": "src/legacy.ts",
189
+ "startLine": 1,
190
+ "endLine": 21,
191
+ "name": "decide",
192
+ "exceeded": [
193
+ { "metric": "functionCognitiveComplexity", "value": 24, "limit": 15 },
194
+ { "metric": "functionCyclomaticComplexity", "value": 15, "limit": 10 },
195
+ { "metric": "functionNestingDepth", "value": 5, "limit": 4 }
196
+ ]
197
+ },
198
+ {
199
+ "kind": "duplication",
200
+ "file": "src/pasted.ts",
201
+ "startLine": 1,
202
+ "endLine": 12,
203
+ "exceeded": [{ "metric": "duplicateLines", "value": 12, "limit": 10 }],
204
+ "partners": [{ "file": "src/report.ts", "startLine": 1, "endLine": 12 }]
205
+ }
206
+ ],
207
+ "errors": [],
208
+ "warnings": []
209
+ }
76
210
  ```
77
211
 
78
- `code-gauge diff --base <ref>` measures every changed file at both revisions (working tree vs. the
79
- merge-base of `<ref>` and `HEAD`, read with `git cat-file` — no checkout, no persisted baseline, so
80
- it works identically in CI and locally) and reports **only violations**. When every gate passes it
81
- prints a single line and exits 0; violations print one line each — the `file:line` span, the metric
82
- with its value and allowed bound (base → head for ratchets), and a remediation direction — and exit
83
- 1 (2 when files cannot be measured).
84
-
85
- Functions are matched across revisions by name and arity, then by name alone, and finally by
86
- normalized-token LCS similarity, so renames and moves don't appear as delete+add. The gates:
87
-
88
- - **Existing functions — no worsening.** A matched function must not worsen its cognitive
89
- complexity, NCSS, max nesting depth, DepDegree (approximate def-use pairs, Beyer & Fararooy
90
- 2010), or Halstead volume beyond a small configurable tolerance. No absolute threshold is
91
- involved: "your change made this function worse" is defensible regardless of metric calibration.
92
- - **New functions — absolute thresholds.** Functions with no base counterpart are the one place
93
- absolutes are required (SonarQube-style "Clean as You Code"): cognitive complexity ≤ 15, NCSS
94
- ≤ 60, nesting depth ≤ 4 by default.
95
- - **No new duplication.** The changed lines of a file must not newly duplicate code, within the
96
- file or against the whole project (so copy-paste from unchanged code is caught where it was
97
- pasted). Only added lines count, and only those whose normalized tokens a partner file repeats:
98
- a file whose untouched code gained a copy elsewhere is not blamed for it, and an edit between
99
- lines already duplicated with the same files, a clone rewritten in place against the same
100
- files, or a clone moved elsewhere in the change is not new duplication, while code pasted next
101
- to an existing clone, or replacing one with a copy of unrelated code, is. Added lines come from
102
- `git diff`, like the hunks reviewers see.
103
- - **Anti-gaming backstops.** Splitting a function resets its entity identity and could hide a
104
- worsening behind the laxer new-code thresholds, so when a removed named function's content
105
- partially reappears in unmatched new code the file's max cognitive complexity and total NCSS
106
- ratchet too; purely additive changes and unrelated remove-plus-add changes stay ungated.
107
-
108
- `--full` additionally prints the base → head values of every checked function; `--json` prints a
109
- machine-readable report. The duplication flags and `--include-tests` work like the ranking command.
110
- The duplication universes contain only git-visible files (tracked or untracked non-ignored), so
111
- local ignored artifacts cannot skew the counts. [Excluded files](#excluded-files) are neither gated
112
- nor part of the universes; a base blob is judged by the git attributes of the merge-base, so code
113
- that stops being generated gates as new code. The Halstead volume allowance scales with the
114
- base value (25%, floored at the configured tolerance) so it admits the same ~5-statement edit at
115
- every function size.
212
+ - `passed`: `true` when there are no violations and no errors (exit code 0).
213
+ - `base`, `mergeBase`: the `--base` ref and the commit compared against; absent without `--base`.
214
+ - `summary`: the violation counts in total and per kind, and the number of files and functions
215
+ checked: those under the target or, with `--base`, the changed files under it and the functions
216
+ the change touches.
217
+ - `violations`: ordered by `file`, then `startLine`. Each has
218
+ - `kind`: `function`, `file`, or `duplication`;
219
+ - `file`, `startLine`, `endLine`: the 1-based line span of the function or duplicated block; for
220
+ kind `file`, the first and last line of the file;
221
+ - `name`: only for kind `function`; `<anonymous>` for a function without a name;
222
+ - `exceeded`: every violated threshold with its `metric` (the config key without its `max` or
223
+ `min` prefix, e.g. `functionNcss` for `maxFunctionNcss`), the measured `value`, and the `limit`
224
+ in force. A value violates when it is above the limit; `duplicateLines` violates from the limit
225
+ on;
226
+ - `partners`: only for kind `duplication`; every other copy of the block, ordered by `file`, then
227
+ `startLine`.
228
+ - `errors`: the files the check covers that could not be measured (exit code 2).
229
+ - `warnings`: files measured without cross-file duplication data, and unmeasured files of the
230
+ repository the check does not cover.
116
231
 
117
232
  ## Configuration
118
233
 
@@ -127,22 +242,19 @@ with `--config`). The following config reproduces every built-in default:
127
242
  "minSimilarityPercent": 70
128
243
  },
129
244
  "rank": { "top": 10 },
130
- "gate": {
131
- "newFunction": {
132
- "maxCognitiveComplexity": 15,
133
- "maxNcss": 60,
134
- "maxNestingDepth": 4
135
- },
136
- "tolerance": {
137
- "cognitiveComplexity": 2,
138
- "ncss": 5,
139
- "nestingDepth": 1,
140
- "depDegree": 10,
141
- "halsteadVolume": 150,
142
- "fileNcss": 20,
143
- "duplicateLines": 0
144
- },
145
- "matchSimilarityPercent": 70
245
+ "thresholds": {
246
+ "maxFunctionCognitiveComplexity": 15,
247
+ "maxFunctionCyclomaticComplexity": 10,
248
+ "maxFunctionNcss": 60,
249
+ "maxFunctionNestingDepth": 4,
250
+ "maxFunctionParameterCount": 7,
251
+ "maxFunctionHalsteadVolume": 2000,
252
+ "maxFunctionHalsteadDifficulty": 20,
253
+ "maxFunctionHalsteadEffort": 30000,
254
+ "maxFunctionDepDegree": 50,
255
+ "maxFileNcss": 500,
256
+ "minDuplicateLines": 10,
257
+ "languages": {}
146
258
  },
147
259
  "exclude": [],
148
260
  "includeTests": false,
@@ -151,11 +263,29 @@ with `--config`). The following config reproduces every built-in default:
151
263
  ```
152
264
 
153
265
  The command line wins over the config file, which wins over the defaults. Unknown settings are
154
- rejected so stale configuration fails loudly.
266
+ rejected so stale configuration fails loudly. `rank.top` and `failOnError` apply to the ranking
267
+ command only.
268
+
269
+ `thresholds.languages` maps a language name (`javascript`, `jsx`, `typescript`, `tsx`, `python`,
270
+ `go`, `rust`, `java`, `ruby`, `c`, `cpp`, `csharp`, `kotlin`) to the thresholds that differ for its
271
+ files; a threshold it leaves out keeps the value set outside `languages`:
272
+
273
+ ```json
274
+ {
275
+ "thresholds": {
276
+ "maxFunctionNcss": 60,
277
+ "maxFunctionHalsteadVolume": null,
278
+ "languages": {
279
+ "tsx": { "maxFunctionNcss": 100 },
280
+ "python": { "maxFunctionParameterCount": null }
281
+ }
282
+ }
283
+ }
284
+ ```
155
285
 
156
286
  ### Excluded files
157
287
 
158
- Directory scans and the regression gate skip, besides the built-in directories and file names:
288
+ Every command skips, besides the built-in directories and file names:
159
289
 
160
290
  - files matching an `exclude` glob pattern (e.g. `"exclude": ["src/legacy/**", "**/*.pb.ts"]`),
161
291
  relative to the config file's directory and matched with Node's `path.posix.matchesGlob` (forward slashes on every platform);
@@ -166,6 +296,9 @@ Directory scans and the regression gate skip, besides the built-in directories a
166
296
  `// Code generated by <tool>. DO NOT EDIT.`). Mark a hand-written file `-linguist-generated` to
167
297
  keep it measured despite such a line.
168
298
 
299
+ Inside a git repository, `code-gauge check` additionally skips the files git ignores (see
300
+ [Threshold check](#threshold-check-code-gauge-check)).
301
+
169
302
  A file passed as the explicit target is always measured.
170
303
 
171
304
  ### Duplication detection settings
@@ -209,8 +342,7 @@ The `duplication` section tunes how clones are detected:
209
342
  - Per-function cyclomatic complexity (McCabe, own body only), counted as NIST SP 500-235 defines it:
210
343
  every decision and short-circuit operator adds one, one per case-labelled statement, plus the
211
344
  file total over McCabe's components (every function, every initializer block, decisions outside
212
- functions, and the module body of a file that runs top-level code); an API-only metric that
213
- ranking and the regression gate ignore
345
+ functions, and the module body of a file that runs top-level code)
214
346
  - Per-function parameter counts and locations (name, node type, line span)
215
347
  - Within-file duplication: copy-pasted blocks matched on normalized tokens (identifiers anonymized
216
348
  consistently, literals by kind, and literal-dense data tables excluded unless their values also
@@ -221,16 +353,16 @@ The `duplication` section tunes how clones are detected:
221
353
  - Cross-file duplication (via `measureCrossFileDuplication`): copy-pasted blocks shared between
222
354
  files, matched with the same normalization (exact, gapped, and near-miss clones) and reported as
223
355
  groups with their file locations
224
- - Halstead base counts, vocabulary, length, volume, and effort, per function and per file — the
225
- strongest correlates of measured cognitive load in the EEG/fMRI validation literature
356
+ - Halstead base counts, vocabulary, length, volume, difficulty (half the distinct operators times
357
+ the total operands per distinct operand; 0 without operands), and effort (difficulty times
358
+ volume), per function and per file
226
359
  - Per-function DepDegree (Beyer & Fararooy 2010), approximated as the number of variable reads
227
360
  with a preceding same-name definition (declaration, assignment, or parameter) in the function —
228
- a file-local single-assignment approximation that is stable enough for regression ratcheting
361
+ a file-local single-assignment approximation
229
362
 
230
363
  Metrics that the validation literature shows to be weakly grounded or that invite misdirected
231
364
  "improvements" (call-graph fan-in/fan-out, coupling and cohesion counts, maintainability index,
232
- and similar) are intentionally not measured, and cyclomatic complexity is kept out of the CLI
233
- output for the same reason; see
365
+ and similar) are intentionally not measured; see
234
366
  [issue #44](https://github.com/WillBooster/code-gauge/issues/44) for the rationale and references.
235
367
 
236
368
  ## Supported languages