fallow 2.98.0 → 2.99.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fallow",
3
- "version": "2.98.0",
3
+ "version": "2.99.0",
4
4
  "description": "Deterministic codebase intelligence for TypeScript and JavaScript. Quality, risk, architecture, dependencies, duplication, and safe cleanup evidence for humans, CI, and agents. Optional runtime intelligence layer (Fallow Runtime) adds production execution evidence. Rust-native, sub-second, zero-config framework support.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -83,13 +83,13 @@
83
83
  "@tanstack/intent": "0.0.42"
84
84
  },
85
85
  "optionalDependencies": {
86
- "@fallow-cli/darwin-arm64": "2.98.0",
87
- "@fallow-cli/darwin-x64": "2.98.0",
88
- "@fallow-cli/linux-x64-gnu": "2.98.0",
89
- "@fallow-cli/linux-arm64-gnu": "2.98.0",
90
- "@fallow-cli/linux-x64-musl": "2.98.0",
91
- "@fallow-cli/linux-arm64-musl": "2.98.0",
92
- "@fallow-cli/win32-arm64-msvc": "2.98.0",
93
- "@fallow-cli/win32-x64-msvc": "2.98.0"
86
+ "@fallow-cli/darwin-arm64": "2.99.0",
87
+ "@fallow-cli/darwin-x64": "2.99.0",
88
+ "@fallow-cli/linux-x64-gnu": "2.99.0",
89
+ "@fallow-cli/linux-arm64-gnu": "2.99.0",
90
+ "@fallow-cli/linux-x64-musl": "2.99.0",
91
+ "@fallow-cli/linux-arm64-musl": "2.99.0",
92
+ "@fallow-cli/win32-arm64-msvc": "2.99.0",
93
+ "@fallow-cli/win32-x64-msvc": "2.99.0"
94
94
  }
95
95
  }
package/schema.json CHANGED
@@ -178,6 +178,7 @@
178
178
  "coverage-gaps": "off",
179
179
  "feature-flags": "off",
180
180
  "stale-suppressions": "warn",
181
+ "require-suppression-reason": "off",
181
182
  "unused-catalog-entries": "warn",
182
183
  "empty-catalog-groups": "warn",
183
184
  "unresolved-catalog-references": "error",
@@ -1139,6 +1140,11 @@
1139
1140
  "$ref": "#/$defs/Severity",
1140
1141
  "default": "warn"
1141
1142
  },
1143
+ "require-suppression-reason": {
1144
+ "description": "Opt-in suppression hygiene rule: when enabled, every `fallow-ignore-*`\ncomment and `@expected-unused` tag must carry a `-- <reason>` suffix.",
1145
+ "$ref": "#/$defs/Severity",
1146
+ "default": "off"
1147
+ },
1142
1148
  "unused-catalog-entries": {
1143
1149
  "$ref": "#/$defs/Severity",
1144
1150
  "default": "warn"
@@ -1939,6 +1945,16 @@
1939
1945
  }
1940
1946
  ]
1941
1947
  },
1948
+ "require-suppression-reason": {
1949
+ "anyOf": [
1950
+ {
1951
+ "$ref": "#/$defs/Severity"
1952
+ },
1953
+ {
1954
+ "type": "null"
1955
+ }
1956
+ ]
1957
+ },
1942
1958
  "unused-catalog-entries": {
1943
1959
  "anyOf": [
1944
1960
  {
@@ -172,8 +172,8 @@ Run `fallow <command> --help` for the full flag list per command (see also refer
172
172
  | `prop-drilling` | - | - | `// fallow-ignore-next-line prop-drilling` | A React/Preact prop is forwarded unchanged through 3+ pass-through components to a distant consumer; Opt-in: set rules.prop-drilling to warn or error to enable. Defaults to off. |
173
173
  | `thin-wrapper` | - | - | `// fallow-ignore-next-line thin-wrapper` | A React/Preact component whose whole body is a single spread-forwarded child render (a candidate for inlining); Opt-in: set rules.thin-wrapper to warn or error to enable. Defaults to off. |
174
174
  | `duplicate-prop-shape` | - | - | `// fallow-ignore-next-line duplicate-prop-shape` | Three or more React/Preact components across two or more files declare an identical prop-name set (a missing shared Props type); Opt-in: set rules.duplicate-prop-shape to warn or error to enable. Defaults to off. |
175
- | `route-collision` | - | - | - | Two or more Next.js App Router route files resolve to the same URL |
176
- | `dynamic-segment-name-conflict` | - | - | - | Sibling Next.js dynamic route segments use different slug names at the same position |
175
+ | `route-collision` | - | - | `// fallow-ignore-file route-collision` | Two or more Next.js App Router route files resolve to the same URL |
176
+ | `dynamic-segment-name-conflict` | - | - | `// fallow-ignore-file dynamic-segment-name-conflict` | Sibling Next.js dynamic route segments use different slug names at the same position |
177
177
  | `high-cyclomatic-complexity` | `--complexity` | - | `// fallow-ignore-next-line complexity` | Function has high cyclomatic complexity |
178
178
  | `high-cognitive-complexity` | `--complexity` | - | `// fallow-ignore-next-line complexity` | Function has high cognitive complexity |
179
179
  | `high-complexity` | `--complexity` | - | `// fallow-ignore-next-line complexity` | Function exceeds both complexity thresholds |
@@ -504,7 +504,7 @@ fallow health --format json --quiet --trend
504
504
  {
505
505
  "kind": "health",
506
506
  "schema_version": 7,
507
- "version": "2.98.0",
507
+ "version": "2.99.0",
508
508
  "elapsed_ms": 32,
509
509
  "summary": {
510
510
  "files_analyzed": 482,
@@ -891,7 +891,7 @@ fallow audit \
891
891
  {
892
892
  "kind": "audit",
893
893
  "schema_version": 7,
894
- "version": "2.98.0",
894
+ "version": "2.99.0",
895
895
  "command": "audit",
896
896
  "verdict": "fail",
897
897
  "changed_files_count": 12,
@@ -966,7 +966,7 @@ fallow flags --format json --quiet --workspace my-package
966
966
  ```json
967
967
  {
968
968
  "schema_version": 7,
969
- "version": "2.98.0",
969
+ "version": "2.99.0",
970
970
  "elapsed_ms": 116,
971
971
  "feature_flags": [],
972
972
  "total_flags": 0
@@ -1066,7 +1066,7 @@ fallow security --gate newly-reachable --changed-since origin/main
1066
1066
  {
1067
1067
  "kind": "security",
1068
1068
  "schema_version": "4",
1069
- "version": "2.98.0",
1069
+ "version": "2.99.0",
1070
1070
  "elapsed_ms": 42,
1071
1071
  "config": {
1072
1072
  "rules": {
@@ -1095,7 +1095,7 @@ fallow security --gate newly-reachable --changed-since origin/main
1095
1095
  {
1096
1096
  "kind": "security",
1097
1097
  "schema_version": "4",
1098
- "version": "2.98.0",
1098
+ "version": "2.99.0",
1099
1099
  "elapsed_ms": 42,
1100
1100
  "config": {
1101
1101
  "rules": {
@@ -1712,7 +1712,7 @@ The HTTP layer mirrors the bash `gh_api_retry` / `curl_retry` helpers: `FALLOW_A
1712
1712
  {
1713
1713
  "kind": "dead-code",
1714
1714
  "schema_version": 7,
1715
- "version": "2.98.0",
1715
+ "version": "2.99.0",
1716
1716
  "elapsed_ms": 45,
1717
1717
  "total_issues": 12,
1718
1718
  "entry_points": {
@@ -1872,7 +1872,7 @@ When `--baseline` is used in combined output, the JSON includes a `baseline_delt
1872
1872
  {
1873
1873
  "kind": "dupes",
1874
1874
  "schema_version": 7,
1875
- "version": "2.98.0",
1875
+ "version": "2.99.0",
1876
1876
  "elapsed_ms": 82,
1877
1877
  "total_clones": 15,
1878
1878
  "total_lines_duplicated": 230,
@@ -1916,11 +1916,11 @@ When running `fallow` with no subcommand (all analyses), the JSON output combine
1916
1916
  {
1917
1917
  "kind": "combined",
1918
1918
  "schema_version": 7,
1919
- "version": "2.98.0",
1919
+ "version": "2.99.0",
1920
1920
  "elapsed_ms": 159,
1921
1921
  "check": {
1922
1922
  "schema_version": 7,
1923
- "version": "2.98.0",
1923
+ "version": "2.99.0",
1924
1924
  "elapsed_ms": 45,
1925
1925
  "total_issues": 12,
1926
1926
  "unused_files": [],
@@ -138,10 +138,12 @@ export type AuditGate = ("new-only" | "all")
138
138
  * Current per-instance flips:
139
139
  *
140
140
  * - `remove-catalog-entry` (`unused-catalog-entries`): `true` only when the
141
- * finding's `hardcoded_consumers` array is empty. When a workspace
142
- * package still pins a hardcoded version of the same package, `fallow fix`
143
- * skips the entry to avoid breaking `pnpm install`, and the action is
144
- * emitted with `auto_fixable: false`.
141
+ * finding's `hardcoded_consumers` array is empty and the source is
142
+ * `pnpm-workspace.yaml`. When a workspace package still pins a hardcoded
143
+ * version of the same package, `fallow fix` skips the entry to avoid
144
+ * breaking `pnpm install`. Bun `package.json` catalog entries are also
145
+ * emitted with `auto_fixable: false` because the current fixer is
146
+ * YAML-only.
145
147
  * - `remove-dependency` vs `move-dependency` (dependency findings): when the
146
148
  * finding's `used_in_workspaces` array is non-empty, the primary action
147
149
  * flips to `move-dependency` with `auto_fixable: false` (`fallow fix` will
@@ -172,7 +174,7 @@ export type IssueAction = (FixAction | SuppressLineAction | SuppressFileAction |
172
174
  * Discriminant string for [`FixAction`]. Kebab-case per the JSON output
173
175
  * contract.
174
176
  */
175
- export type FixActionType = ("remove-export" | "delete-file" | "remove-dependency" | "move-dependency" | "remove-enum-member" | "remove-class-member" | "resolve-import" | "install-dependency" | "remove-duplicate" | "move-to-dev" | "refactor-cycle" | "refactor-re-export-cycle" | "refactor-boundary" | "export-type" | "remove-catalog-entry" | "remove-empty-catalog-group" | "update-catalog-reference" | "add-catalog-entry" | "remove-catalog-reference" | "remove-dependency-override" | "fix-dependency-override" | "resolve-policy-violation" | "move-to-server-module" | "split-mixed-barrel" | "hoist-directive" | "wire-server-action" | "provide-inject" | "use-load-data" | "render-component" | "use-component-prop" | "emit-component-event" | "wire-svelte-event" | "resolve-route-collision" | "resolve-dynamic-segment-name-conflict")
177
+ export type FixActionType = ("remove-export" | "delete-file" | "remove-dependency" | "move-dependency" | "remove-enum-member" | "remove-class-member" | "resolve-import" | "install-dependency" | "remove-duplicate" | "move-to-dev" | "refactor-cycle" | "refactor-re-export-cycle" | "refactor-boundary" | "export-type" | "remove-catalog-entry" | "remove-empty-catalog-group" | "update-catalog-reference" | "add-catalog-entry" | "remove-catalog-reference" | "remove-dependency-override" | "fix-dependency-override" | "resolve-policy-violation" | "move-to-server-module" | "split-mixed-barrel" | "hoist-directive" | "wire-server-action" | "provide-inject" | "use-load-data" | "render-component" | "use-component-prop" | "emit-component-event" | "wire-svelte-event" | "resolve-route-collision" | "resolve-dynamic-segment-name-conflict" | "add-suppression-reason" | "remove-stale-suppression")
176
178
  /**
177
179
  * Singleton discriminant for [`SuppressLineAction`].
178
180
  */
@@ -253,6 +255,10 @@ export type SuppressionOrigin = ({
253
255
  * The issue kind token from the comment (e.g., "unused-exports"), or None for blanket.
254
256
  */
255
257
  issue_kind?: (string | null)
258
+ /**
259
+ * Human-authored reason after `--`, when present.
260
+ */
261
+ reason?: (string | null)
256
262
  /**
257
263
  * Whether this was a file-level suppression.
258
264
  */
@@ -272,6 +278,10 @@ type: "comment"
272
278
  * The name of the export that was tagged.
273
279
  */
274
280
  export_name: string
281
+ /**
282
+ * Human-authored reason after `--`, when present.
283
+ */
284
+ reason?: (string | null)
275
285
  type: "jsdoc_tag"
276
286
  })
277
287
  /**
@@ -1032,24 +1042,25 @@ policy_violations?: PolicyViolationFinding[]
1032
1042
  */
1033
1043
  stale_suppressions?: StaleSuppression[]
1034
1044
  /**
1035
- * Entries in pnpm-workspace.yaml's catalog: or catalogs: sections not
1036
- * referenced by any workspace package via the catalog: protocol. Wrapped
1037
- * in [`UnusedCatalogEntryFinding`] so each entry carries a typed
1045
+ * Entries in package manager catalog sections not referenced by any
1046
+ * workspace package via the catalog: protocol. Supports
1047
+ * `pnpm-workspace.yaml` catalogs and Bun root `package.json` catalogs.
1048
+ * Wrapped in [`UnusedCatalogEntryFinding`] so each entry carries a typed
1038
1049
  * `actions` array natively, with per-instance `auto_fixable` derived
1039
- * from `hardcoded_consumers`.
1050
+ * from `hardcoded_consumers` and the catalog source file.
1040
1051
  */
1041
1052
  unused_catalog_entries?: UnusedCatalogEntryFinding[]
1042
1053
  /**
1043
- * Named groups under pnpm-workspace.yaml's catalogs: section that declare
1044
- * no package entries. The top-level catalog: map is not reported. Wrapped
1045
- * in [`EmptyCatalogGroupFinding`].
1054
+ * Named groups under package manager catalogs sections that declare no
1055
+ * package entries. The top-level catalog: map is not reported. Wrapped in
1056
+ * [`EmptyCatalogGroupFinding`].
1046
1057
  */
1047
1058
  empty_catalog_groups?: EmptyCatalogGroupFinding[]
1048
1059
  /**
1049
1060
  * Workspace package.json references to catalogs (`catalog:` or
1050
- * `catalog:<name>`) that do not declare the consumed package. pnpm install
1051
- * will error until the named catalog grows to include the package or the
1052
- * reference is switched / removed. Wrapped in
1061
+ * `catalog:<name>`) that do not declare the consumed package. The package
1062
+ * manager install will error until the named catalog grows to include the
1063
+ * package or the reference is switched / removed. Wrapped in
1053
1064
  * [`UnresolvedCatalogReferenceFinding`] with the discriminated
1054
1065
  * `add-catalog-entry` / `update-catalog-reference` primary at position 0.
1055
1066
  */
@@ -1452,11 +1463,12 @@ type: FixActionType
1452
1463
  * FINDING, not per action type: the same `type` may carry
1453
1464
  * `auto_fixable: true` on one finding and `auto_fixable: false` on
1454
1465
  * another when per-instance guards in the applier discriminate (e.g.
1455
- * `remove-catalog-entry` flips on `hardcoded_consumers`, the primary
1456
- * dependency action flips between `remove-dependency` /
1457
- * `move-dependency` on `used_in_workspaces`). Filter on this bool of
1458
- * each individual action, not on `type`. See the [`IssueAction`]
1459
- * enum-level docs for the full list of per-instance flips.
1466
+ * `remove-catalog-entry` flips on `hardcoded_consumers` and catalog
1467
+ * source file, the primary dependency action flips between
1468
+ * `remove-dependency` / `move-dependency` on `used_in_workspaces`).
1469
+ * Filter on this bool of each individual action, not on `type`. See the
1470
+ * [`IssueAction`] enum-level docs for the full list of per-instance
1471
+ * flips.
1460
1472
  */
1461
1473
  auto_fixable: boolean
1462
1474
  /**
@@ -2428,12 +2440,20 @@ line: number
2428
2440
  */
2429
2441
  col: number
2430
2442
  origin: SuppressionOrigin
2443
+ /**
2444
+ * True when `rules.require-suppression-reason` reported a suppression
2445
+ * comment or tag that has no reason.
2446
+ */
2447
+ missing_reason?: boolean
2448
+ /**
2449
+ * Suggested next steps. Always emitted.
2450
+ */
2451
+ actions: IssueAction[]
2431
2452
  }
2432
2453
  /**
2433
2454
  * Wire-shape envelope for an [`UnusedCatalogEntry`] finding. Per-instance
2434
- * `auto_fixable` flips to `false` when `hardcoded_consumers` is non-empty:
2435
- * the entry cannot be removed safely while a workspace package still pins
2436
- * the same package via a hardcoded version range.
2455
+ * `auto_fixable` flips to `false` when `hardcoded_consumers` is non-empty or
2456
+ * the source is not `pnpm-workspace.yaml`.
2437
2457
  */
2438
2458
  export interface UnusedCatalogEntryFinding {
2439
2459
  /**
@@ -2441,16 +2461,16 @@ export interface UnusedCatalogEntryFinding {
2441
2461
  */
2442
2462
  entry_name: string
2443
2463
  /**
2444
- * Catalog group: `"default"` for the top-level `catalog:` map, or the
2445
- * named catalog key for entries declared under `catalogs.<name>:`.
2464
+ * Catalog group: `"default"` for the default catalog map, or the named
2465
+ * catalog key for entries declared under `catalogs.<name>`.
2446
2466
  */
2447
2467
  catalog_name: string
2448
2468
  /**
2449
- * Path to `pnpm-workspace.yaml`, relative to the analyzed root.
2469
+ * Path to the catalog source file, relative to the analyzed root.
2450
2470
  */
2451
2471
  path: string
2452
2472
  /**
2453
- * 1-based line number of the catalog entry within `pnpm-workspace.yaml`.
2473
+ * 1-based line number of the catalog entry within the source file.
2454
2474
  */
2455
2475
  line: number
2456
2476
  /**
@@ -2472,20 +2492,20 @@ introduced?: (AuditIntroduced | null)
2472
2492
  }
2473
2493
  /**
2474
2494
  * Wire-shape envelope for an [`EmptyCatalogGroup`] finding. Carries a
2475
- * straightforward `remove-empty-catalog-group` primary plus a YAML-comment
2476
- * suppress.
2495
+ * `remove-empty-catalog-group` primary. YAML-sourced findings also include a
2496
+ * YAML-comment suppress action.
2477
2497
  */
2478
2498
  export interface EmptyCatalogGroupFinding {
2479
2499
  /**
2480
- * Catalog group name declared under the top-level `catalogs:` map.
2500
+ * Catalog group name declared under the `catalogs` map.
2481
2501
  */
2482
2502
  catalog_name: string
2483
2503
  /**
2484
- * Path to `pnpm-workspace.yaml`, relative to the analyzed root.
2504
+ * Path to the catalog source file, relative to the analyzed root.
2485
2505
  */
2486
2506
  path: string
2487
2507
  /**
2488
- * 1-based line number of the empty group header within `pnpm-workspace.yaml`.
2508
+ * 1-based line number of the empty group header within the source file.
2489
2509
  */
2490
2510
  line: number
2491
2511
  /**
@@ -2531,10 +2551,10 @@ path: string
2531
2551
  */
2532
2552
  line: number
2533
2553
  /**
2534
- * Other catalogs (in the same `pnpm-workspace.yaml`) that DO declare this
2535
- * package. Empty when no catalog has the package. Sorted lexicographically.
2536
- * Lets agents and humans decide whether to switch the reference to a
2537
- * different catalog or to add the entry to the named catalog.
2554
+ * Other catalogs in the same catalog source that DO declare this package.
2555
+ * Empty when no catalog has the package. Sorted lexicographically. Lets
2556
+ * agents and humans decide whether to switch the reference to a different
2557
+ * catalog or to add the entry to the named catalog.
2538
2558
  */
2539
2559
  available_in_catalogs?: string[]
2540
2560
  /**
@@ -7117,24 +7137,25 @@ policy_violations?: PolicyViolationFinding[]
7117
7137
  */
7118
7138
  stale_suppressions?: StaleSuppression[]
7119
7139
  /**
7120
- * Entries in pnpm-workspace.yaml's catalog: or catalogs: sections not
7121
- * referenced by any workspace package via the catalog: protocol. Wrapped
7122
- * in [`UnusedCatalogEntryFinding`] so each entry carries a typed
7140
+ * Entries in package manager catalog sections not referenced by any
7141
+ * workspace package via the catalog: protocol. Supports
7142
+ * `pnpm-workspace.yaml` catalogs and Bun root `package.json` catalogs.
7143
+ * Wrapped in [`UnusedCatalogEntryFinding`] so each entry carries a typed
7123
7144
  * `actions` array natively, with per-instance `auto_fixable` derived
7124
- * from `hardcoded_consumers`.
7145
+ * from `hardcoded_consumers` and the catalog source file.
7125
7146
  */
7126
7147
  unused_catalog_entries?: UnusedCatalogEntryFinding[]
7127
7148
  /**
7128
- * Named groups under pnpm-workspace.yaml's catalogs: section that declare
7129
- * no package entries. The top-level catalog: map is not reported. Wrapped
7130
- * in [`EmptyCatalogGroupFinding`].
7149
+ * Named groups under package manager catalogs sections that declare no
7150
+ * package entries. The top-level catalog: map is not reported. Wrapped in
7151
+ * [`EmptyCatalogGroupFinding`].
7131
7152
  */
7132
7153
  empty_catalog_groups?: EmptyCatalogGroupFinding[]
7133
7154
  /**
7134
7155
  * Workspace package.json references to catalogs (`catalog:` or
7135
- * `catalog:<name>`) that do not declare the consumed package. pnpm install
7136
- * will error until the named catalog grows to include the package or the
7137
- * reference is switched / removed. Wrapped in
7156
+ * `catalog:<name>`) that do not declare the consumed package. The package
7157
+ * manager install will error until the named catalog grows to include the
7158
+ * package or the reference is switched / removed. Wrapped in
7138
7159
  * [`UnresolvedCatalogReferenceFinding`] with the discriminated
7139
7160
  * `add-catalog-entry` / `update-catalog-reference` primary at position 0.
7140
7161
  */