fallow 3.21.0 → 3.22.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.21.0",
3
+ "version": "3.22.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": [
@@ -6004,10 +6004,11 @@
6004
6004
  "FALLOW_CACHE_DIR": "Directory for fallow's persistent analysis cache. Relative paths resolve from the project root and override cache.dir.",
6005
6005
  "FALLOW_CACHE_MAX_SIZE": "Extraction cache size cap in megabytes (default 256). Wins over the cache.maxSizeMb config field.",
6006
6006
  "FALLOW_EXTENDS_TIMEOUT_SECS": "Timeout in seconds for fetching https:// configs referenced via the extends field (default 5).",
6007
- "FALLOW_COVERAGE": "Path to Istanbul coverage data (coverage-final.json) for accurate per-function CRAP scores. CLI --coverage flag overrides this; it wins over the health.coverage config field. Honored by the health, bare fallow, and audit CLI commands and by the MCP audit and check_health tools on both their typed route and their CLI fallback, where the explicit coverage parameter overrides it.",
6008
- "FALLOW_COVERAGE_ROOT": "Absolute coverage-data path prefix for rebasing Istanbul paths in CI or containers. CLI --coverage-root flag overrides this; it wins over the health.coverageRoot config field. Honored by the health, bare fallow, and audit CLI commands and by the MCP audit and check_health tools on both their typed route and their CLI fallback, where the explicit coverage_root parameter overrides it.",
6007
+ "FALLOW_COVERAGE": "Path to Istanbul coverage data (coverage-final.json) for accurate per-function CRAP scores. CLI --coverage flag overrides this; it wins over the health.coverage config field. Honored by the health, bare fallow, audit, and viz CLI commands and by the MCP audit and check_health tools on both their typed route and their CLI fallback, where the explicit coverage parameter overrides it. Viz reads it through the same precedence and has no coverage flag of its own.",
6008
+ "FALLOW_COVERAGE_ROOT": "Absolute coverage-data path prefix for rebasing Istanbul paths in CI or containers. CLI --coverage-root flag overrides this; it wins over the health.coverageRoot config field. Honored by the health, bare fallow, audit, and viz CLI commands and by the MCP audit and check_health tools on both their typed route and their CLI fallback, where the explicit coverage_root parameter overrides it. Viz reads it through the same precedence and has no coverage flag of its own.",
6009
6009
  "FALLOW_MAX_FILE_SIZE": "Per-file size ceiling in megabytes for source discovery (default 5; 0 = no limit). CLI --max-file-size flag overrides this.",
6010
6010
  "FALLOW_TYPE_AWARE": "Enable or disable TypeScript semantic (type-aware) analysis for the run (true/false/1/0/yes/no/on/off). Precedence: --type-aware/--no-type-aware CLI flags, then FALLOW_TYPE_AWARE, then the audit.typeAware config field, then typeAware.enabled.",
6011
+ "FALLOW_TYPE_AWARE_TIMEOUT_SECS": "Wall clock in seconds allowed for one TypeScript semantic sidecar request (default 120; unset, 0, or a non-numeric value keeps the default). Raise it for very large TypeScript programs, where a semantic query scans the whole program.",
6011
6012
  "FALLOW_TYPE_AWARE_BIN": "Trusted executable override for the TypeScript semantic refinement sidecar used by dead-code --type-aware. Relative paths resolve from the caller's working directory before --root is applied; project node_modules and PATH are intentionally not searched. Default: sibling of the active Fallow executable.",
6012
6013
  "FALLOW_AUDIT_BASE": "Pins the fallow audit comparison base ref when no --base/--changed-since is passed (e.g. upstream/main).",
6013
6014
  "FALLOW_AUDIT_CACHE_MAX_AGE_DAYS": "GC threshold in days for reusable audit base-snapshot caches (default 30; 0 disables the sweep).",
@@ -6524,7 +6525,8 @@
6524
6525
  ],
6525
6526
  "license": "free",
6526
6527
  "license_note": null,
6527
- "read_only": true
6528
+ "read_only": true,
6529
+ "code_mode_alias": null
6528
6530
  },
6529
6531
  {
6530
6532
  "name": "analyze",
@@ -6541,7 +6543,8 @@
6541
6543
  ],
6542
6544
  "license": "free",
6543
6545
  "license_note": null,
6544
- "read_only": true
6546
+ "read_only": true,
6547
+ "code_mode_alias": "analyze"
6545
6548
  },
6546
6549
  {
6547
6550
  "name": "check_changed",
@@ -6555,7 +6558,8 @@
6555
6558
  ],
6556
6559
  "license": "free",
6557
6560
  "license_note": null,
6558
- "read_only": true
6561
+ "read_only": true,
6562
+ "code_mode_alias": "checkChanged"
6559
6563
  },
6560
6564
  {
6561
6565
  "name": "security_candidates",
@@ -6570,7 +6574,8 @@
6570
6574
  ],
6571
6575
  "license": "free",
6572
6576
  "license_note": null,
6573
- "read_only": true
6577
+ "read_only": true,
6578
+ "code_mode_alias": "securityCandidates"
6574
6579
  },
6575
6580
  {
6576
6581
  "name": "find_similar_code",
@@ -6586,7 +6591,8 @@
6586
6591
  ],
6587
6592
  "license": "free",
6588
6593
  "license_note": null,
6589
- "read_only": true
6594
+ "read_only": true,
6595
+ "code_mode_alias": null
6590
6596
  },
6591
6597
  {
6592
6598
  "name": "inspect_similar_code",
@@ -6599,7 +6605,8 @@
6599
6605
  ],
6600
6606
  "license": "free",
6601
6607
  "license_note": null,
6602
- "read_only": true
6608
+ "read_only": true,
6609
+ "code_mode_alias": null
6603
6610
  },
6604
6611
  {
6605
6612
  "name": "inspect_target",
@@ -6613,7 +6620,8 @@
6613
6620
  ],
6614
6621
  "license": "free",
6615
6622
  "license_note": null,
6616
- "read_only": true
6623
+ "read_only": true,
6624
+ "code_mode_alias": null
6617
6625
  },
6618
6626
  {
6619
6627
  "name": "guard",
@@ -6625,7 +6633,8 @@
6625
6633
  ],
6626
6634
  "license": "free",
6627
6635
  "license_note": null,
6628
- "read_only": true
6636
+ "read_only": true,
6637
+ "code_mode_alias": null
6629
6638
  },
6630
6639
  {
6631
6640
  "name": "find_dupes",
@@ -6642,7 +6651,8 @@
6642
6651
  ],
6643
6652
  "license": "free",
6644
6653
  "license_note": null,
6645
- "read_only": true
6654
+ "read_only": true,
6655
+ "code_mode_alias": "findDupes"
6646
6656
  },
6647
6657
  {
6648
6658
  "name": "check_health",
@@ -6662,7 +6672,8 @@
6662
6672
  ],
6663
6673
  "license": "free",
6664
6674
  "license_note": null,
6665
- "read_only": true
6675
+ "read_only": true,
6676
+ "code_mode_alias": "checkHealth"
6666
6677
  },
6667
6678
  {
6668
6679
  "name": "check_runtime_coverage",
@@ -6678,7 +6689,8 @@
6678
6689
  ],
6679
6690
  "license": "freemium",
6680
6691
  "license_note": "A single local runtime-coverage capture is free; continuous or multi-capture runtime monitoring requires an active license (fallow license activate).",
6681
- "read_only": true
6692
+ "read_only": true,
6693
+ "code_mode_alias": "checkRuntimeCoverage"
6682
6694
  },
6683
6695
  {
6684
6696
  "name": "get_hot_paths",
@@ -6692,7 +6704,8 @@
6692
6704
  ],
6693
6705
  "license": "freemium",
6694
6706
  "license_note": "A single local runtime-coverage capture is free; continuous or multi-capture runtime monitoring requires an active license (fallow license activate).",
6695
- "read_only": true
6707
+ "read_only": true,
6708
+ "code_mode_alias": "getHotPaths"
6696
6709
  },
6697
6710
  {
6698
6711
  "name": "get_blast_radius",
@@ -6705,7 +6718,8 @@
6705
6718
  ],
6706
6719
  "license": "freemium",
6707
6720
  "license_note": "A single local runtime-coverage capture is free; continuous or multi-capture runtime monitoring requires an active license (fallow license activate).",
6708
- "read_only": true
6721
+ "read_only": true,
6722
+ "code_mode_alias": "getBlastRadius"
6709
6723
  },
6710
6724
  {
6711
6725
  "name": "get_importance",
@@ -6718,7 +6732,8 @@
6718
6732
  ],
6719
6733
  "license": "freemium",
6720
6734
  "license_note": "A single local runtime-coverage capture is free; continuous or multi-capture runtime monitoring requires an active license (fallow license activate).",
6721
- "read_only": true
6735
+ "read_only": true,
6736
+ "code_mode_alias": "getImportance"
6722
6737
  },
6723
6738
  {
6724
6739
  "name": "get_cleanup_candidates",
@@ -6731,7 +6746,8 @@
6731
6746
  ],
6732
6747
  "license": "freemium",
6733
6748
  "license_note": "A single local runtime-coverage capture is free; continuous or multi-capture runtime monitoring requires an active license (fallow license activate).",
6734
- "read_only": true
6749
+ "read_only": true,
6750
+ "code_mode_alias": "getCleanupCandidates"
6735
6751
  },
6736
6752
  {
6737
6753
  "name": "get_token_blast_radius",
@@ -6741,7 +6757,8 @@
6741
6757
  "key_params": [],
6742
6758
  "license": "free",
6743
6759
  "license_note": null,
6744
- "read_only": true
6760
+ "read_only": true,
6761
+ "code_mode_alias": null
6745
6762
  },
6746
6763
  {
6747
6764
  "name": "audit",
@@ -6758,7 +6775,8 @@
6758
6775
  ],
6759
6776
  "license": "free",
6760
6777
  "license_note": null,
6761
- "read_only": true
6778
+ "read_only": true,
6779
+ "code_mode_alias": "audit"
6762
6780
  },
6763
6781
  {
6764
6782
  "name": "decision_surface",
@@ -6772,7 +6790,8 @@
6772
6790
  ],
6773
6791
  "license": "free",
6774
6792
  "license_note": null,
6775
- "read_only": true
6793
+ "read_only": true,
6794
+ "code_mode_alias": null
6776
6795
  },
6777
6796
  {
6778
6797
  "name": "fallow_explain",
@@ -6784,7 +6803,8 @@
6784
6803
  ],
6785
6804
  "license": "free",
6786
6805
  "license_note": null,
6787
- "read_only": true
6806
+ "read_only": true,
6807
+ "code_mode_alias": "explain"
6788
6808
  },
6789
6809
  {
6790
6810
  "name": "fix_preview",
@@ -6796,7 +6816,8 @@
6796
6816
  ],
6797
6817
  "license": "free",
6798
6818
  "license_note": null,
6799
- "read_only": true
6819
+ "read_only": true,
6820
+ "code_mode_alias": null
6800
6821
  },
6801
6822
  {
6802
6823
  "name": "fix_apply",
@@ -6808,7 +6829,8 @@
6808
6829
  ],
6809
6830
  "license": "free",
6810
6831
  "license_note": null,
6811
- "read_only": false
6832
+ "read_only": false,
6833
+ "code_mode_alias": null
6812
6834
  },
6813
6835
  {
6814
6836
  "name": "project_info",
@@ -6823,7 +6845,8 @@
6823
6845
  ],
6824
6846
  "license": "free",
6825
6847
  "license_note": null,
6826
- "read_only": true
6848
+ "read_only": true,
6849
+ "code_mode_alias": "projectInfo"
6827
6850
  },
6828
6851
  {
6829
6852
  "name": "recommend",
@@ -6835,7 +6858,8 @@
6835
6858
  ],
6836
6859
  "license": "free",
6837
6860
  "license_note": null,
6838
- "read_only": true
6861
+ "read_only": true,
6862
+ "code_mode_alias": null
6839
6863
  },
6840
6864
  {
6841
6865
  "name": "list_boundaries",
@@ -6845,7 +6869,8 @@
6845
6869
  "key_params": [],
6846
6870
  "license": "free",
6847
6871
  "license_note": null,
6848
- "read_only": true
6872
+ "read_only": true,
6873
+ "code_mode_alias": "listBoundaries"
6849
6874
  },
6850
6875
  {
6851
6876
  "name": "feature_flags",
@@ -6858,7 +6883,8 @@
6858
6883
  ],
6859
6884
  "license": "free",
6860
6885
  "license_note": null,
6861
- "read_only": true
6886
+ "read_only": true,
6887
+ "code_mode_alias": "featureFlags"
6862
6888
  },
6863
6889
  {
6864
6890
  "name": "list_suppressions",
@@ -6872,7 +6898,8 @@
6872
6898
  ],
6873
6899
  "license": "free",
6874
6900
  "license_note": null,
6875
- "read_only": true
6901
+ "read_only": true,
6902
+ "code_mode_alias": null
6876
6903
  },
6877
6904
  {
6878
6905
  "name": "impact",
@@ -6884,7 +6911,8 @@
6884
6911
  ],
6885
6912
  "license": "free",
6886
6913
  "license_note": null,
6887
- "read_only": true
6914
+ "read_only": true,
6915
+ "code_mode_alias": "impact"
6888
6916
  },
6889
6917
  {
6890
6918
  "name": "impact_all",
@@ -6897,7 +6925,8 @@
6897
6925
  ],
6898
6926
  "license": "free",
6899
6927
  "license_note": null,
6900
- "read_only": true
6928
+ "read_only": true,
6929
+ "code_mode_alias": null
6901
6930
  },
6902
6931
  {
6903
6932
  "name": "trace_export",
@@ -6910,7 +6939,8 @@
6910
6939
  ],
6911
6940
  "license": "free",
6912
6941
  "license_note": null,
6913
- "read_only": true
6942
+ "read_only": true,
6943
+ "code_mode_alias": "traceExport"
6914
6944
  },
6915
6945
  {
6916
6946
  "name": "trace_symbol",
@@ -6925,7 +6955,8 @@
6925
6955
  ],
6926
6956
  "license": "free",
6927
6957
  "license_note": null,
6928
- "read_only": true
6958
+ "read_only": true,
6959
+ "code_mode_alias": null
6929
6960
  },
6930
6961
  {
6931
6962
  "name": "symbol_impact",
@@ -6942,7 +6973,8 @@
6942
6973
  ],
6943
6974
  "license": "free",
6944
6975
  "license_note": null,
6945
- "read_only": true
6976
+ "read_only": true,
6977
+ "code_mode_alias": null
6946
6978
  },
6947
6979
  {
6948
6980
  "name": "trace_file",
@@ -6954,7 +6986,8 @@
6954
6986
  ],
6955
6987
  "license": "free",
6956
6988
  "license_note": null,
6957
- "read_only": true
6989
+ "read_only": true,
6990
+ "code_mode_alias": "traceFile"
6958
6991
  },
6959
6992
  {
6960
6993
  "name": "impact_closure",
@@ -6966,7 +6999,8 @@
6966
6999
  ],
6967
7000
  "license": "free",
6968
7001
  "license_note": null,
6969
- "read_only": true
7002
+ "read_only": true,
7003
+ "code_mode_alias": "impactClosure"
6970
7004
  },
6971
7005
  {
6972
7006
  "name": "trace_dependency",
@@ -6978,7 +7012,8 @@
6978
7012
  ],
6979
7013
  "license": "free",
6980
7014
  "license_note": null,
6981
- "read_only": true
7015
+ "read_only": true,
7016
+ "code_mode_alias": "traceDependency"
6982
7017
  },
6983
7018
  {
6984
7019
  "name": "trace_clone",
@@ -6994,7 +7029,8 @@
6994
7029
  ],
6995
7030
  "license": "free",
6996
7031
  "license_note": null,
6997
- "read_only": true
7032
+ "read_only": true,
7033
+ "code_mode_alias": "traceClone"
6998
7034
  }
6999
7035
  ]
7000
7036
  },
@@ -7069,7 +7105,7 @@
7069
7105
  ]
7070
7106
  },
7071
7107
  "plugins": {
7072
- "count": 124,
7108
+ "count": 125,
7073
7109
  "note": "Built-in framework plugins, auto-activated when their enabler dependency is present; run fallow list --plugins for the set active in a specific project",
7074
7110
  "names": [
7075
7111
  "nextjs",
@@ -7195,7 +7231,8 @@
7195
7231
  "wuchale",
7196
7232
  "varlock",
7197
7233
  "pnpm",
7198
- "bun"
7234
+ "bun",
7235
+ "deno"
7199
7236
  ]
7200
7237
  },
7201
7238
  "task_matrix": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fallow",
3
- "version": "3.21.0",
3
+ "version": "3.22.0",
4
4
  "mcpName": "io.github.fallow-rs/fallow",
5
5
  "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.",
6
6
  "license": "MIT",
@@ -88,14 +88,14 @@
88
88
  "@tanstack/intent": "0.3.6"
89
89
  },
90
90
  "optionalDependencies": {
91
- "@fallow-cli/darwin-arm64": "3.21.0",
92
- "@fallow-cli/darwin-x64": "3.21.0",
93
- "@fallow-cli/linux-x64-gnu": "3.21.0",
94
- "@fallow-cli/linux-arm64-gnu": "3.21.0",
95
- "@fallow-cli/linux-x64-musl": "3.21.0",
96
- "@fallow-cli/linux-arm64-musl": "3.21.0",
97
- "@fallow-cli/win32-arm64-msvc": "3.21.0",
98
- "@fallow-cli/win32-x64-msvc": "3.21.0",
99
- "fallow-type-aware": "3.21.0"
91
+ "@fallow-cli/darwin-arm64": "3.22.0",
92
+ "@fallow-cli/darwin-x64": "3.22.0",
93
+ "@fallow-cli/linux-x64-gnu": "3.22.0",
94
+ "@fallow-cli/linux-arm64-gnu": "3.22.0",
95
+ "@fallow-cli/linux-x64-musl": "3.22.0",
96
+ "@fallow-cli/linux-arm64-musl": "3.22.0",
97
+ "@fallow-cli/win32-arm64-msvc": "3.22.0",
98
+ "@fallow-cli/win32-x64-msvc": "3.22.0",
99
+ "fallow-type-aware": "3.22.0"
100
100
  }
101
101
  }
package/schema.json CHANGED
@@ -12,6 +12,13 @@
12
12
  ],
13
13
  "writeOnly": true
14
14
  },
15
+ "minimumVersion": {
16
+ "description": "The lowest fallow version this config is written for, as `MAJOR.MINOR.PATCH`. An older binary stops with a message naming both versions instead of failing on the first field it does not recognize, so a config committed to a shared repository says which upgrade it needs rather than reading like a typo. Optional and unset by default; set it in the same change that adds a field introduced in a newer version. It gates nothing on its own: an unknown key still fails on a binary at or above this version, because there the key really is a typo.",
17
+ "type": [
18
+ "string",
19
+ "null"
20
+ ]
21
+ },
15
22
  "extends": {
16
23
  "description": "An ordered array of parent config sources to inherit before this file's own keys apply; each entry is a file-relative path, an `npm:<package>` specifier, or an `https://` URL (`http://` is rejected), deep-merged in order so objects merge field-by-field while arrays and scalars in this file replace the parent's, with cycle and depth guards. Set it to share a base config across a monorepo or team; it is consumed at load and stripped before serialization (serde skip_serializing).",
17
24
  "type": "array",
@@ -1067,7 +1074,7 @@
1067
1074
  "default": 60
1068
1075
  },
1069
1076
  "coverage": {
1070
- "description": "Path to Istanbul-format coverage data for accurate per-function CRAP\nscores. Relative paths resolve against the project root. The CLI\n`--coverage` flag and `FALLOW_COVERAGE` environment variable override\nthis value. Consulted by `fallow health`, bare `fallow`, `fallow audit`,\nand the MCP `audit` / `check_health` tools.",
1077
+ "description": "Path to Istanbul-format coverage data for accurate per-function CRAP\nscores. Relative paths resolve against the project root. The CLI\n`--coverage` flag and `FALLOW_COVERAGE` environment variable override\nthis value. Consulted by `fallow health`, bare `fallow`, `fallow audit`,\n`fallow viz`, and the MCP `audit` / `check_health` tools.",
1071
1078
  "type": [
1072
1079
  "string",
1073
1080
  "null"
@@ -1075,7 +1082,7 @@
1075
1082
  "default": null
1076
1083
  },
1077
1084
  "coverageRoot": {
1078
- "description": "Absolute prefix to strip from Istanbul file paths before CRAP matching.\nUse when coverage was generated under a different checkout root in CI\nor Docker. The CLI `--coverage-root` flag and `FALLOW_COVERAGE_ROOT`\nenvironment variable override this value. Consulted by `fallow health`,\nbare `fallow`, `fallow audit`, and the MCP `audit` / `check_health`\ntools.",
1085
+ "description": "Absolute prefix to strip from Istanbul file paths before CRAP matching.\nUse when coverage was generated under a different checkout root in CI\nor Docker. The CLI `--coverage-root` flag and `FALLOW_COVERAGE_ROOT`\nenvironment variable override this value. Consulted by `fallow health`,\nbare `fallow`, `fallow audit`, `fallow viz`, and the MCP `audit` /\n`check_health` tools.",
1079
1086
  "type": [
1080
1087
  "string",
1081
1088
  "null"
@@ -1132,7 +1139,7 @@
1132
1139
  }
1133
1140
  },
1134
1141
  "functions": {
1135
- "description": "Exact emitted function names this override applies to. Empty means every\nfunction in matching files.",
1142
+ "description": "Exact emitted function names this override applies to. Empty means every\nfunction in matching files. Synthetic units are matched by their emitted\nname, so `\"<template>\"` and `\"<snippet:NAME>\"` are valid keys. The\nsynthetic `\"<module>\"` unit is not: it carries a file's module-scope\ndecision points into the aggregate metrics and never produces a finding,\nso there is no ceiling for an override to move.",
1136
1143
  "type": "array",
1137
1144
  "items": {
1138
1145
  "type": "string"
@@ -125,7 +125,7 @@ Route by intent before reaching for the big analysis commands. Same matrix as `f
125
125
  | `coverage` | Runtime coverage setup, focused analysis, and cloud inventory workflow helper | `setup`, `setup --yes`, `setup --non-interactive`, `analyze --runtime-coverage <path>`, `analyze --cloud --repo owner/repo`, `upload-inventory` |
126
126
  | `coverage upload-source-maps` | Upload build source maps from CI so bundled runtime coverage resolves to original source paths. Retries 429 `Retry-After` and transient gateway failures. Use `FALLOW_CA_BUNDLE` for complete custom PEM trust bundles. | `--dir dist`, `--git-sha <sha>`, `--repo <name>`, `--strip-path=false`, `--dry-run` |
127
127
  | `setup-hooks` | Deprecated (removed in the next major): use `agent install` or `hooks install --target agent`; still installs the Claude Code PreToolUse gate with a stderr warning | `--agent`, `--dry-run`, `--force`, `--user`, `--gitignore-claude`, `--uninstall` |
128
- | `viz` | Render the codebase as a self-contained interactive HTML map (treemap + import graph, four lenses: dead code, duplication, boundaries, complexity, with click-through detail panels), or emit the import graph as text. Read-only. | `--out <path>`, `--no-open`, `--viz-format html\|dot\|mermaid`, `--root`, `--config`, `--production`, `--no-cache` |
128
+ | `viz` | Render the codebase as a self-contained interactive HTML map (treemap + import graph) with six primary lenses (Overview, Unused, Duplication, Architecture, Health, Security) and Dependencies, Frameworks, Styling, and Feature flags under an adaptive More menu, each with click-through detail panels. Every lens carries an availability state (complete, disabled, not applicable, unavailable) next to its count, so an analysis that did not run reads as missing data instead of as zero findings. Or emit the import graph as text. Read-only. | `--out <path>`, `--no-open`, `--viz-format html\|dot\|mermaid`, `--root`, `--config`, `--production`, `--no-cache` |
129
129
 
130
130
  Run `fallow <command> --help` for the full flag list per command (see also references/cli-reference.md).
131
131
  <!-- generated:commands:end -->
@@ -385,7 +385,7 @@ Human output groups paths under "Shared with your team (commit these)" and "Loca
385
385
  {
386
386
  "kind": "agent-install",
387
387
  "schema_version": 1,
388
- "fallow_version": "3.20.0",
388
+ "fallow_version": "3.22.0",
389
389
  "root": "/abs/path",
390
390
  "mode": "install",
391
391
  "dry_run": false,
@@ -589,7 +589,7 @@ fallow health --format json --quiet --trend
589
589
  {
590
590
  "kind": "health",
591
591
  "schema_version": 7,
592
- "version": "3.21.0",
592
+ "version": "3.22.0",
593
593
  "elapsed_ms": 32,
594
594
  "summary": {
595
595
  "files_analyzed": 482,
@@ -987,7 +987,7 @@ fallow audit \
987
987
  {
988
988
  "kind": "audit",
989
989
  "schema_version": 7,
990
- "version": "3.21.0",
990
+ "version": "3.22.0",
991
991
  "command": "audit",
992
992
  "verdict": "fail",
993
993
  "changed_files_count": 12,
@@ -1064,7 +1064,7 @@ fallow flags --format json --quiet --workspace my-package
1064
1064
  ```json
1065
1065
  {
1066
1066
  "schema_version": 7,
1067
- "version": "3.21.0",
1067
+ "version": "3.22.0",
1068
1068
  "elapsed_ms": 116,
1069
1069
  "feature_flags": [],
1070
1070
  "total_flags": 0
@@ -1165,7 +1165,7 @@ fallow security --gate newly-reachable --changed-since origin/main
1165
1165
  {
1166
1166
  "kind": "security",
1167
1167
  "schema_version": "4",
1168
- "version": "3.21.0",
1168
+ "version": "3.22.0",
1169
1169
  "elapsed_ms": 42,
1170
1170
  "config": {
1171
1171
  "rules": {
@@ -1194,7 +1194,7 @@ fallow security --gate newly-reachable --changed-since origin/main
1194
1194
  {
1195
1195
  "kind": "security",
1196
1196
  "schema_version": "4",
1197
- "version": "3.21.0",
1197
+ "version": "3.22.0",
1198
1198
  "elapsed_ms": 42,
1199
1199
  "config": {
1200
1200
  "rules": {
@@ -1959,7 +1959,7 @@ The HTTP layer mirrors the bash `gh_api_retry` / `curl_retry` helpers: `FALLOW_A
1959
1959
  {
1960
1960
  "kind": "dead-code",
1961
1961
  "schema_version": 7,
1962
- "version": "3.21.0",
1962
+ "version": "3.22.0",
1963
1963
  "elapsed_ms": 45,
1964
1964
  "total_issues": 12,
1965
1965
  "entry_points": {
@@ -2119,7 +2119,7 @@ When `--baseline` is used in combined output, the JSON includes a `baseline_delt
2119
2119
  {
2120
2120
  "kind": "dupes",
2121
2121
  "schema_version": 7,
2122
- "version": "3.21.0",
2122
+ "version": "3.22.0",
2123
2123
  "elapsed_ms": 82,
2124
2124
  "total_clones": 15,
2125
2125
  "total_lines_duplicated": 230,
@@ -2163,11 +2163,11 @@ When running `fallow` with no subcommand (all analyses), the JSON output combine
2163
2163
  {
2164
2164
  "kind": "combined",
2165
2165
  "schema_version": 7,
2166
- "version": "3.21.0",
2166
+ "version": "3.22.0",
2167
2167
  "elapsed_ms": 159,
2168
2168
  "check": {
2169
2169
  "schema_version": 7,
2170
- "version": "3.21.0",
2170
+ "version": "3.22.0",
2171
2171
  "elapsed_ms": 45,
2172
2172
  "total_issues": 12,
2173
2173
  "unused_files": [],
@@ -11,7 +11,7 @@ When using fallow via MCP (`fallow-mcp`), the following tools are available:
11
11
  <!-- generated:mcp-tools:start -->
12
12
  | Tool | Kind | License | CLI fallback | Key params | Description |
13
13
  |---|---|---|---|---|---|
14
- | `code_execute` | composition | free | - | `code`, `timeout_ms`, `max_output_bytes` | Bounded read-only Code Mode for composing multiple fallow analysis calls in one JavaScript snippet. The snippet receives `{ fallow, root }`, returns JSON-serializable data, and can call read-only helpers such as `fallow.projectInfo`, `fallow.audit`, `fallow.checkHealth`, and `fallow.run(tool, params)` for the same allowlist. Similar-code is excluded because Code Mode is capped at 30 seconds; use standalone `find_similar_code` and `inspect_similar_code`, which have dedicated 15-minute timeouts. Mutating fix tools are not exposed. The sandbox has no filesystem, network, imports, `eval`, `Function`, `process`, `require`, `Deno`, `Bun`, or shell access. Params: `code`, optional `root`, `timeout_ms` (capped at 30000), and `max_output_bytes` (capped at 4000000). |
14
+ | `code_execute` | composition | free | - | `code`, `timeout_ms`, `max_output_bytes` | Bounded read-only Code Mode for composing multiple fallow analysis calls in one JavaScript snippet. The snippet receives `{ fallow, root }`, returns JSON-serializable data, and can call read-only helpers such as `fallow.projectInfo`, `fallow.audit`, `fallow.checkHealth`, and `fallow.run(tool, params)` for the same allowlist. `fallow.all(requests)` fans out independent calls in one go: pass `[{ tool, params }, ...]` and get back a positionally aligned array of `{ ok: true, value }` or `{ ok: false, error }`, so one failing element never hides the rest. Host calls are memoized for the duration of one snippet, so repeating the same tool with the same params (key order does not matter) is served from cache, spends no `max_host_calls` slot and no output budget, and is reported in `calls[]` with `cache_hit: true`; a call refused before dispatch (unknown tool, malformed params) spends no slot either, and `limits.max_rejected_host_calls` bounds how many of those the response records. Similar-code is excluded because Code Mode is capped at 30 seconds; use standalone `find_similar_code` and `inspect_similar_code`, which have dedicated 15-minute timeouts. Mutating fix tools are not exposed. The sandbox has no filesystem, network, imports, `process`, `require`, `Deno`, `Bun`, or shell access, and no dynamic code compilation: `eval`, `Function`, and the async and generator function constructors are removed, including the `constructor` route reachable through function prototypes. Params: `code`, optional `root`, `timeout_ms` (capped at 30000), and `max_output_bytes` (capped at 4000000). `max_output_bytes` bounds two separate things: the total fallow JSON host calls read, shared across a `fallow.all` fan-out rather than granted per element, and the serialized snippet result. An oversized result is refused with `ok:false`, `truncated:true`, `result_bytes`, and a short `result_preview` in place of the value, never returned whole, so return a projection rather than a whole report. |
15
15
  | `analyze` | analysis | free | `fallow dead-code --format json --quiet` | `issue_types`, `production`, `workspace`, `baseline`, `group_by`, `file` | Full dead code analysis (unused files/exports/types/dependencies/members + circular dependencies + re-export cycles (barrel files that form a structural loop, silently breaking re-exports) + boundary violations + rule-pack policy violations (banned calls, imports, and catalogue-derived effects declared via the `rulePacks` config key) + stale suppressions). Private type leaks are an opt-in API hygiene check via `issue_types: ["private-type-leaks"]`. Set `boundary_violations: true` as a convenience alias for `issue_types: ["boundary-violations"]`. Set `group_by` to `"owner"`, `"directory"`, `"package"`, or `"section"` to partition results. The `section` mode reads GitLab CODEOWNERS `[Section]` headers and emits `owners` metadata per group |
16
16
  | `check_changed` | analysis | free | `fallow dead-code --changed-since <ref> --format json --quiet` | `since`, `baseline`, `fail_on_regression` | Incremental analysis of files changed since a git ref |
17
17
  | `security_candidates` | analysis | free | `fallow security --format json --quiet` | `gate`, `surface`, `changed_since`, `paths` | Unverified local security candidates, not confirmed vulnerabilities (`fallow security --format json`). Read `security_findings[]` for category, CWE, severity, evidence, trace, optional `reachability`, blind-spot counters, and optional `unresolved_callee_diagnostics` samples for dynamic callee follow-up. `severity` is a review-priority tier, not a verified vulnerability verdict. Each finding also carries an agent-actionable `candidate` (`source_kind`/`sink`/`boundary`), where URL-category sinks may include `url_shape` (`fixed-origin-dynamic-path` or `dynamic-origin`), an optional `taint_flow` source-to-sink triple, and a stable `finding_id` (equal to the SARIF fingerprint) for cross-run correlation; there is no `impact` field (deciding exploitability is the agent's job). Set `surface: true` to include top-level `attack_surface[]` entries with defensive-boundary prompts for a verifier. Set `gate` to `new` for changed-line candidates or `newly-reachable` for candidates that became reachable from entry points; `newly-reachable` requires `changed_since`. `reachability.untrusted_source_trace` is module-level import context only and does not prove value flow; `reachability.taint_confidence` tiers each reachable candidate as `arg-level` (sink argument traces to a same-module source read, strong) or `module-level` (only the module is import-reachable from a source, weak), so tier from this field instead of the evidence text. Verify trace, reachability context, severity, and evidence before editing code. Supports `root`, `config`, `workspace`, `paths`, `changed_since`, `changed_workspaces`, `surface`, `gate`, `no_cache`, and `threads`; `paths` forwards repeated `fallow security --file` filters for finding anchors, trace hops, untrusted-source reachability trace hops, and unresolved-callee diagnostics. See <https://docs.fallow.tools/cli/security-agent-verification> for the verifier packet and verdict recipe. Inherits `FALLOW_DIFF_FILE` from the server environment for line-level diff scoping; raise `FALLOW_TIMEOUT_SECS` for large repos. |
@@ -1026,6 +1026,10 @@ export type ConfidenceFlag = ("dynamic-dispatch" | "re-export-indirection")
1026
1026
  * The category of a single weakening signal.
1027
1027
  */
1028
1028
  export type WeakeningKind = ("test-weakened" | "threshold-lowered" | "suppression-added" | "security-check-removed")
1029
+ /**
1030
+ * Where a cognitive-complexity improvement came from.
1031
+ */
1032
+ export type CognitiveAttribution = ("nesting-reset" | "fewer-branch-points" | "mixed")
1029
1033
  /**
1030
1034
  * Independently-versioned wire-version newtype. Serializes as the integer
1031
1035
  * [`DECISION_SURFACE_SCHEMA_VERSION`].
@@ -5698,7 +5702,13 @@ export interface HealthSummary {
5698
5702
  */
5699
5703
  files_analyzed: number
5700
5704
  /**
5701
- * Functions scored across the analyzed files.
5705
+ * Real functions scored across the analyzed files. This counts functions
5706
+ * only, so it is smaller than the sum of `file_scores[].function_count`,
5707
+ * which also counts the synthetic per-file units (`<module>` for
5708
+ * module-scope branching, `<template>` and `<snippet:NAME>` for component
5709
+ * templates). The complexity aggregates below, including
5710
+ * `average_cyclomatic` and `p90_cyclomatic`, are computed over that larger
5711
+ * population rather than over this count.
5702
5712
  */
5703
5713
  functions_analyzed: number
5704
5714
  /**
@@ -12361,6 +12371,11 @@ duplication?: (DupesReportPayload | null)
12361
12371
  * Complexity findings scoped to the audit changeset.
12362
12372
  */
12363
12373
  complexity?: (HealthReport | null)
12374
+ /**
12375
+ * Branching conservation across the changeset. Absent when no base
12376
+ * comparison ran.
12377
+ */
12378
+ branching?: (BranchingReport | null)
12364
12379
  }
12365
12380
  /**
12366
12381
  * The ranked, capped decision surface plus the set of signal_ids the
@@ -12776,6 +12791,200 @@ expert: string[]
12776
12791
  */
12777
12792
  bus_factor_one?: boolean
12778
12793
  }
12794
+ /**
12795
+ * The brief's branching section.
12796
+ */
12797
+ export interface BranchingReport {
12798
+ /**
12799
+ * Files carrying the split signature: branching within `tolerance` of
12800
+ * where it was, more functions, a smaller largest function. Empty when
12801
+ * none do, which is the common case and is not itself a finding. See
12802
+ * `SplitInPlace` for why this describes a shape rather than asserting a
12803
+ * refactor.
12804
+ */
12805
+ split_in_place: SplitInPlace[]
12806
+ /**
12807
+ * The band inside which a file's branching counts as held. Published
12808
+ * because a claim against an unpublished threshold is not reproducible by
12809
+ * a consumer.
12810
+ */
12811
+ tolerance: number
12812
+ scope: BranchingScope
12813
+ branch_points: BranchingMetric
12814
+ functions: BranchingMetric
12815
+ peak_unit_cyclomatic: BranchingMetric
12816
+ /**
12817
+ * Base-side branch points of the files that have no head entry.
12818
+ */
12819
+ branch_points_only_in_base: number
12820
+ cognitive: BranchingCognitive
12821
+ /**
12822
+ * The files that moved the numbers most, largest absolute branch-point
12823
+ * change first.
12824
+ */
12825
+ by_file: BranchingFileDelta[]
12826
+ /**
12827
+ * Files with a change that the list did not name.
12828
+ */
12829
+ by_file_omitted: number
12830
+ }
12831
+ /**
12832
+ * One file present on both revisions whose branching held while it gained
12833
+ * functions and its largest function shrank.
12834
+ *
12835
+ * Local by construction: nothing here depends on any other file, so unrelated
12836
+ * work in the changeset cannot make it more or less true. A set-level
12837
+ * classifier cannot make this claim, because a changeset contains arbitrary
12838
+ * other work and an aggregate cannot attribute.
12839
+ *
12840
+ * It is a description, not an inference. The three conditions are the
12841
+ * signature a split leaves, and they are also satisfiable without one: the
12842
+ * peak is a file-level maximum (`FileBranching::peak_cyclomatic`), so it can
12843
+ * fall because the largest function left the file while arriving helpers
12844
+ * happen to carry the branching it took with it. Both numbers are reported so
12845
+ * a reader can see that for themselves, and the rendered text states what was
12846
+ * measured rather than concluding a refactor happened.
12847
+ *
12848
+ * Files carrying synthetic template units are excluded, because those units
12849
+ * are outside every count here, so the numbers would not describe the file a
12850
+ * reader opens. Test paths are excluded too: their totals are reported in
12851
+ * `BranchingScope` instead.
12852
+ */
12853
+ export interface SplitInPlace {
12854
+ /**
12855
+ * Root-relative path.
12856
+ */
12857
+ path: string
12858
+ /**
12859
+ * Branch points on the base revision.
12860
+ */
12861
+ branch_points_before: number
12862
+ /**
12863
+ * And on head. Within `tolerance` of `branch_points_before`, which is what
12864
+ * "held" means here. Both are reported because one number alone cannot be
12865
+ * checked.
12866
+ */
12867
+ branch_points_after: number
12868
+ /**
12869
+ * Accounted functions before the split.
12870
+ */
12871
+ functions_before: number
12872
+ /**
12873
+ * Accounted functions after it.
12874
+ */
12875
+ functions_after: number
12876
+ /**
12877
+ * Highest single-function cyclomatic score before.
12878
+ */
12879
+ peak_before: number
12880
+ /**
12881
+ * And after. It falls by construction when a function is split, which is
12882
+ * why it is evidence here and never a metric to celebrate.
12883
+ */
12884
+ peak_after: number
12885
+ }
12886
+ /**
12887
+ * Size and composition of the compared set.
12888
+ */
12889
+ export interface BranchingScope {
12890
+ /**
12891
+ * Files carrying units on both revisions.
12892
+ */
12893
+ files_both: number
12894
+ /**
12895
+ * Files carrying units on the head revision only.
12896
+ */
12897
+ files_added: number
12898
+ /**
12899
+ * Files that carried units on the base revision only, whether they were
12900
+ * deleted or merely lost every accounted unit. Reported, and excluded from
12901
+ * every headline number: such a file contributes its whole base-side total
12902
+ * as a fall with no head counterpart.
12903
+ */
12904
+ files_only_in_base: number
12905
+ /**
12906
+ * Branch points on test-shaped paths within the head totals. Test code
12907
+ * routinely dominates both terms, so a reader needs to see its share
12908
+ * before reading the headline.
12909
+ */
12910
+ test_branch_points: number
12911
+ /**
12912
+ * Functions on test-shaped paths within the head totals.
12913
+ */
12914
+ test_functions: number
12915
+ /**
12916
+ * Share of head branch points owned by the single largest file, so a
12917
+ * reader can see when one vendored or generated file owns the number.
12918
+ */
12919
+ largest_file_share_of_branch_points: number
12920
+ }
12921
+ /**
12922
+ * One metric across the two revisions.
12923
+ */
12924
+ export interface BranchingMetric {
12925
+ /**
12926
+ * Value on the base revision.
12927
+ */
12928
+ previous: number
12929
+ /**
12930
+ * Value on the head revision.
12931
+ */
12932
+ current: number
12933
+ /**
12934
+ * `current - previous`. Signed, so a consumer never has to infer direction
12935
+ * from a separate field.
12936
+ */
12937
+ delta: number
12938
+ }
12939
+ /**
12940
+ * The cognitive figure and what drove it.
12941
+ *
12942
+ * `previous` and `current` exclude prop-count and hook-density increments.
12943
+ * Both are cognitive-only, and prop count records an excess over a floor, so
12944
+ * it is superlinear in a split and would move this number with branching and
12945
+ * nesting both flat. This therefore does not match the cognitive score the
12946
+ * complexity findings report.
12947
+ */
12948
+ export interface BranchingCognitive {
12949
+ /**
12950
+ * Cognitive weight on the base revision.
12951
+ */
12952
+ previous: number
12953
+ /**
12954
+ * Cognitive weight on the head revision.
12955
+ */
12956
+ current: number
12957
+ /**
12958
+ * `current - previous`.
12959
+ */
12960
+ delta: number
12961
+ /**
12962
+ * Change in the summed nesting depth behind those increments.
12963
+ */
12964
+ nesting_weight_delta: number
12965
+ /**
12966
+ * What the improvement is attributable to, absent when cognitive did not
12967
+ * fall. There is nothing to attribute when the number rose or held.
12968
+ */
12969
+ attributed_to?: (CognitiveAttribution | null)
12970
+ }
12971
+ /**
12972
+ * One file's contribution to the change.
12973
+ */
12974
+ export interface BranchingFileDelta {
12975
+ /**
12976
+ * Root-relative path.
12977
+ */
12978
+ path: string
12979
+ /**
12980
+ * Change in branch points for this file.
12981
+ */
12982
+ branch_points_delta: number
12983
+ /**
12984
+ * Change in accounted functions for this file.
12985
+ */
12986
+ functions_delta: number
12987
+ }
12779
12988
  /**
12780
12989
  * The separable `decision-surface` envelope: the single call that puts taste-
12781
12990
  * decisions in front of a human, callable WITHOUT the full pipeline (the
@@ -12962,6 +13171,13 @@ deltas: ReviewDeltas
12962
13171
  weakening: WeakeningSignal[]
12963
13172
  routing: RoutingFacts
12964
13173
  decisions: DecisionSurface
13174
+ /**
13175
+ * Branching conservation across the changeset: total branching against
13176
+ * the number of functions now holding it. Absent when no base comparison
13177
+ * ran, which keeps the wire shape byte-identical for a consumer that
13178
+ * never had a base snapshot.
13179
+ */
13180
+ branching?: (BranchingReport | null)
12965
13181
  }
12966
13182
  /**
12967
13183
  * The review direction artifact: the order to review in, the coherent units,