fallow 3.13.0 → 3.14.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.
package/capabilities.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fallow",
3
- "version": "3.13.0",
3
+ "version": "3.14.0",
4
4
  "manifest_version": "1",
5
5
  "description": "Codebase analyzer for TypeScript/JavaScript: unused code, circular dependencies, code duplication, complexity hotspots, and architecture boundary violations",
6
6
  "global_flags": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fallow",
3
- "version": "3.13.0",
3
+ "version": "3.14.0",
4
4
  "description": "Codebase intelligence for TypeScript and JavaScript. Free static analysis of code and styles, optional paid runtime intelligence (Fallow Runtime). Quality, risk, architecture, dependencies, duplication, and design-system drift for humans, CI, and the agents writing your code. Zero-config framework support.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -87,14 +87,14 @@
87
87
  "@tanstack/intent": "0.3.6"
88
88
  },
89
89
  "optionalDependencies": {
90
- "@fallow-cli/darwin-arm64": "3.13.0",
91
- "@fallow-cli/darwin-x64": "3.13.0",
92
- "@fallow-cli/linux-x64-gnu": "3.13.0",
93
- "@fallow-cli/linux-arm64-gnu": "3.13.0",
94
- "@fallow-cli/linux-x64-musl": "3.13.0",
95
- "@fallow-cli/linux-arm64-musl": "3.13.0",
96
- "@fallow-cli/win32-arm64-msvc": "3.13.0",
97
- "@fallow-cli/win32-x64-msvc": "3.13.0",
98
- "fallow-type-aware": "3.13.0"
90
+ "@fallow-cli/darwin-arm64": "3.14.0",
91
+ "@fallow-cli/darwin-x64": "3.14.0",
92
+ "@fallow-cli/linux-x64-gnu": "3.14.0",
93
+ "@fallow-cli/linux-arm64-gnu": "3.14.0",
94
+ "@fallow-cli/linux-x64-musl": "3.14.0",
95
+ "@fallow-cli/linux-arm64-musl": "3.14.0",
96
+ "@fallow-cli/win32-arm64-msvc": "3.14.0",
97
+ "@fallow-cli/win32-x64-msvc": "3.14.0",
98
+ "fallow-type-aware": "3.14.0"
99
99
  }
100
100
  }
package/schema.json CHANGED
@@ -141,7 +141,7 @@
141
141
  }
142
142
  },
143
143
  "health": {
144
- "description": "Sets complexity and health thresholds for `fallow health` (also applied in combined `fallow` and `fallow audit`): `maxCyclomatic` (20), `maxCognitive` (15), `maxCrap` (30.0, findings at or above this are reported), `crapRefactorBand` (5, cyclomatic band below `maxCyclomatic` where a secondary refactor action is added), `maxUnitSize` (max function lines before a large-function finding, 60), `coverage`/`coverageRoot` (Istanbul coverage path and path-prefix strip for accurate CRAP), `ignore` globs (remove files from findings AND the health score), `thresholdOverrides` (per-file/per-function ceilings via `files`/`functions`/`maxCyclomatic`/`maxCognitive`/`maxCrap`/`maxUnitSize`/`reason`), `ownership` (`botPatterns` and `emailMode` for `--ownership`), and `suggestInlineSuppression` (true, emit `suppress-line` action hints in JSON). Raise thresholds to relax which functions are flagged, wire `coverage` for real CRAP scores, or exempt generated/test files via `ignore` (drops them from the score too) or `thresholdOverrides` (keeps them visible under a higher ceiling).",
144
+ "description": "Sets complexity and health thresholds for `fallow health` (also applied in combined `fallow` and `fallow audit`): `maxCyclomatic` (20), `maxCognitive` (15), `maxCrap` (30.0, findings at or above this are reported), `crapRefactorBand` (5, cyclomatic band below `maxCyclomatic` where a secondary refactor action is added), `maxUnitSize` (max function lines before a large-function finding, 60), `coverage`/`coverageRoot` (Istanbul coverage path and path-prefix strip for accurate CRAP), `ignore` globs (remove files from findings AND the health score), `thresholdOverrides` (per-file/per-function ceilings via `files`/`functions`/`maxCyclomatic`/`maxCognitive`/`maxCrap`/`maxUnitSize`/`reason`), `ownership` (`botPatterns` and `emailMode` for `--ownership`), and `suggestInlineSuppression` (true, emit `suppress-line` action hints in JSON). Raise thresholds to relax which functions are flagged, wire `coverage` for real CRAP scores, or exempt generated/test files via `ignore` (drops them from the score too) or `thresholdOverrides` (keeps them visible under a higher ceiling). The four `max*` thresholds govern findings only and never move `health_score`: its penalties use fixed calibration so grades stay comparable across projects, and `ignore` is the lever that removes files from the score.",
145
145
  "$ref": "#/$defs/HealthConfig",
146
146
  "default": {
147
147
  "maxCyclomatic": 20,
@@ -935,7 +935,7 @@
935
935
  "type": "object",
936
936
  "properties": {
937
937
  "maxCyclomatic": {
938
- "description": "Maximum allowed cyclomatic complexity per function (default: 20).\nFunctions exceeding this threshold are reported.",
938
+ "description": "Maximum allowed cyclomatic complexity per function (default: 20).\nFunctions exceeding this threshold are reported. Governs findings\nonly; the health score's complexity penalties use fixed calibration\n(use `health.ignore` to remove a file from the score).",
939
939
  "type": "integer",
940
940
  "format": "uint16",
941
941
  "minimum": 0,
@@ -943,7 +943,7 @@
943
943
  "default": 20
944
944
  },
945
945
  "maxCognitive": {
946
- "description": "Maximum allowed cognitive complexity per function (default: 15).\nFunctions exceeding this threshold are reported.",
946
+ "description": "Maximum allowed cognitive complexity per function (default: 15).\nFunctions exceeding this threshold are reported. Governs findings\nonly, never the health score.",
947
947
  "type": "integer",
948
948
  "format": "uint16",
949
949
  "minimum": 0,
@@ -951,7 +951,7 @@
951
951
  "default": 15
952
952
  },
953
953
  "maxCrap": {
954
- "description": "Maximum allowed CRAP (Change Risk Anti-Patterns) score per function\n(default: 30.0). CRAP combines cyclomatic complexity with test\ncoverage: high complexity plus low coverage produces a high CRAP\nscore. Functions meeting or exceeding this threshold are reported.\nUse `--coverage` with Istanbul data for accurate per-function CRAP;\notherwise fallow estimates coverage from the module graph.",
954
+ "description": "Maximum allowed CRAP (Change Risk Anti-Patterns) score per function\n(default: 30.0). CRAP combines cyclomatic complexity with test\ncoverage: high complexity plus low coverage produces a high CRAP\nscore. Functions meeting or exceeding this threshold are reported.\nUse `--coverage` with Istanbul data for accurate per-function CRAP;\notherwise fallow estimates coverage from the module graph. Governs\nfindings only, never the health score.",
955
955
  "type": "number",
956
956
  "format": "double",
957
957
  "default": 30.0
@@ -965,7 +965,7 @@
965
965
  "default": 5
966
966
  },
967
967
  "maxUnitSize": {
968
- "description": "Maximum function length in lines of code before it is reported as an\noversized \"large function\" (default: 60). Raise it globally, or per file\nvia `thresholdOverrides[].maxUnitSize`, to relax the bar for generated or\ntest files (where a `describe()` block spans hundreds of lines) without\ndisabling complexity checks on those files. This filters the reported\nlarge-functions list only; the descriptive unit-size profile and the\nhealth score still reflect raw sizes (use `health.ignore` to remove a\nfile from the score entirely).",
968
+ "description": "Maximum function length in lines of code before it is reported as an\noversized \"large function\" (default: 60). Raise it globally, or per file\nvia `thresholdOverrides[].maxUnitSize`, to relax the bar for generated or\ntest files (where a `describe()` block spans hundreds of lines) without\ndisabling complexity checks on those files. This filters the reported\nlarge-functions list only; the descriptive unit-size profile and the\nhealth score still reflect raw sizes against fixed calibration (the\n`unit_size` penalty keeps its `>60` LOC very-high-risk edge so grades\nstay comparable across projects; use `health.ignore` to remove a file\nfrom the score entirely).",
969
969
  "type": "integer",
970
970
  "format": "uint32",
971
971
  "minimum": 0,
@@ -145,12 +145,18 @@ function resolveTypeAwareCompanion(
145
145
  }
146
146
  }
147
147
 
148
- function childEnvironment(resolvedVersion) {
148
+ function childEnvironment(resolvedVersion, resolveCompanion = resolveTypeAwareCompanion) {
149
149
  if (process.env.FALLOW_TYPE_AWARE_BIN) return process.env;
150
- const companion = resolveTypeAwareCompanion(resolvedVersion);
150
+ const companion = resolveCompanion(resolvedVersion);
151
151
  return companion === undefined
152
152
  ? process.env
153
- : { ...process.env, FALLOW_TYPE_AWARE_BIN: companion };
153
+ : {
154
+ ...process.env,
155
+ FALLOW_TYPE_AWARE_BIN: companion,
156
+ // Marks the wiring as launcher-provided node_modules resolution so
157
+ // `type-aware status` does not report it as a user-set override.
158
+ FALLOW_TYPE_AWARE_BIN_SOURCE: "npm-wrapper",
159
+ };
154
160
  }
155
161
 
156
162
  // Swallow EPIPE on stdout. When fallow's output is piped into a reader that
@@ -223,4 +229,5 @@ module.exports = {
223
229
  guardBrokenStdout, // test-only
224
230
  exitCodeForChildFailure, // test-only
225
231
  resolveTypeAwareCompanion, // test-only
232
+ childEnvironment, // test-only
226
233
  };
@@ -159,6 +159,39 @@ test("resolveTypeAwareCompanion accepts only an exact matching package", (t) =>
159
159
  assert.equal(resolveTypeAwareCompanion("3.8.0", resolvePackage), undefined);
160
160
  });
161
161
 
162
+ test("childEnvironment marks launcher-wired companions as npm-wrapper", (t) => {
163
+ const { childEnvironment } = require(RUN_BINARY);
164
+ const previousBin = process.env.FALLOW_TYPE_AWARE_BIN;
165
+ delete process.env.FALLOW_TYPE_AWARE_BIN;
166
+ t.after(() => {
167
+ if (previousBin === undefined) delete process.env.FALLOW_TYPE_AWARE_BIN;
168
+ else process.env.FALLOW_TYPE_AWARE_BIN = previousBin;
169
+ });
170
+
171
+ const env = childEnvironment("3.8.0", () => "/tmp/fallow-type-aware.mjs");
172
+ assert.equal(env.FALLOW_TYPE_AWARE_BIN, "/tmp/fallow-type-aware.mjs");
173
+ assert.equal(env.FALLOW_TYPE_AWARE_BIN_SOURCE, "npm-wrapper");
174
+
175
+ // No resolvable companion: the environment passes through untouched.
176
+ const untouched = childEnvironment("3.8.0", () => undefined);
177
+ assert.equal(untouched, process.env);
178
+ assert.equal(untouched.FALLOW_TYPE_AWARE_BIN_SOURCE, undefined);
179
+ });
180
+
181
+ test("childEnvironment leaves a user-set override unmarked", (t) => {
182
+ const { childEnvironment } = require(RUN_BINARY);
183
+ const previousBin = process.env.FALLOW_TYPE_AWARE_BIN;
184
+ process.env.FALLOW_TYPE_AWARE_BIN = "/opt/custom-sidecar";
185
+ t.after(() => {
186
+ if (previousBin === undefined) delete process.env.FALLOW_TYPE_AWARE_BIN;
187
+ else process.env.FALLOW_TYPE_AWARE_BIN = previousBin;
188
+ });
189
+
190
+ const env = childEnvironment("3.8.0", () => "/tmp/fallow-type-aware.mjs");
191
+ assert.equal(env, process.env);
192
+ assert.equal(env.FALLOW_TYPE_AWARE_BIN_SOURCE, undefined);
193
+ });
194
+
162
195
  test(
163
196
  "fallow-lsp executes the multicall binary with the lsp-server subcommand",
164
197
  { skip: process.platform === "win32" },
@@ -517,7 +517,7 @@ fallow health --format json --quiet --trend
517
517
  {
518
518
  "kind": "health",
519
519
  "schema_version": 7,
520
- "version": "3.13.0",
520
+ "version": "3.14.0",
521
521
  "elapsed_ms": 32,
522
522
  "summary": {
523
523
  "files_analyzed": 482,
@@ -915,7 +915,7 @@ fallow audit \
915
915
  {
916
916
  "kind": "audit",
917
917
  "schema_version": 7,
918
- "version": "3.13.0",
918
+ "version": "3.14.0",
919
919
  "command": "audit",
920
920
  "verdict": "fail",
921
921
  "changed_files_count": 12,
@@ -992,7 +992,7 @@ fallow flags --format json --quiet --workspace my-package
992
992
  ```json
993
993
  {
994
994
  "schema_version": 7,
995
- "version": "3.13.0",
995
+ "version": "3.14.0",
996
996
  "elapsed_ms": 116,
997
997
  "feature_flags": [],
998
998
  "total_flags": 0
@@ -1093,7 +1093,7 @@ fallow security --gate newly-reachable --changed-since origin/main
1093
1093
  {
1094
1094
  "kind": "security",
1095
1095
  "schema_version": "4",
1096
- "version": "3.13.0",
1096
+ "version": "3.14.0",
1097
1097
  "elapsed_ms": 42,
1098
1098
  "config": {
1099
1099
  "rules": {
@@ -1122,7 +1122,7 @@ fallow security --gate newly-reachable --changed-since origin/main
1122
1122
  {
1123
1123
  "kind": "security",
1124
1124
  "schema_version": "4",
1125
- "version": "3.13.0",
1125
+ "version": "3.14.0",
1126
1126
  "elapsed_ms": 42,
1127
1127
  "config": {
1128
1128
  "rules": {
@@ -1886,7 +1886,7 @@ The HTTP layer mirrors the bash `gh_api_retry` / `curl_retry` helpers: `FALLOW_A
1886
1886
  {
1887
1887
  "kind": "dead-code",
1888
1888
  "schema_version": 7,
1889
- "version": "3.13.0",
1889
+ "version": "3.14.0",
1890
1890
  "elapsed_ms": 45,
1891
1891
  "total_issues": 12,
1892
1892
  "entry_points": {
@@ -2046,7 +2046,7 @@ When `--baseline` is used in combined output, the JSON includes a `baseline_delt
2046
2046
  {
2047
2047
  "kind": "dupes",
2048
2048
  "schema_version": 7,
2049
- "version": "3.13.0",
2049
+ "version": "3.14.0",
2050
2050
  "elapsed_ms": 82,
2051
2051
  "total_clones": 15,
2052
2052
  "total_lines_duplicated": 230,
@@ -2090,11 +2090,11 @@ When running `fallow` with no subcommand (all analyses), the JSON output combine
2090
2090
  {
2091
2091
  "kind": "combined",
2092
2092
  "schema_version": 7,
2093
- "version": "3.13.0",
2093
+ "version": "3.14.0",
2094
2094
  "elapsed_ms": 159,
2095
2095
  "check": {
2096
2096
  "schema_version": 7,
2097
- "version": "3.13.0",
2097
+ "version": "3.14.0",
2098
2098
  "elapsed_ms": 45,
2099
2099
  "total_issues": 12,
2100
2100
  "unused_files": [],
@@ -5650,6 +5650,13 @@ maintainability?: (number | null)
5650
5650
  hotspots?: (number | null)
5651
5651
  unused_deps?: (number | null)
5652
5652
  circular_deps?: (number | null)
5653
+ /**
5654
+ * Penalty for oversized functions, computed against fixed calibration
5655
+ * (very-high-risk bin edge at 60 LOC). Deliberately independent of
5656
+ * `health.maxUnitSize`, which filters the large-functions findings list
5657
+ * only; raising that threshold empties the list without moving this
5658
+ * penalty. `health.ignore` removes files from the score entirely.
5659
+ */
5653
5660
  unit_size?: (number | null)
5654
5661
  coupling?: (number | null)
5655
5662
  duplication?: (number | null)