Repository navigation
v3.32.0: grouped health trends, markdown from saved runs, dead-code accuracy fixes
Features
fallow dead-code --absent-component-propsreports optional props that callers never pass. The rule reports an optional component prop that no known reachable caller passes. It is off by default. It supports React, Preact, Solid, Qwik, Vue, Svelte, Astro, Angular, Lit and Ember components. Each finding shows the declaration, the callers, the framework and whether a default exists. Fallow gives no finding when it cannot see every caller of the component. Static analysis does not prove that a prop is unreachable at runtime. Review defaults and API intent manually. The rule has no automatic fix.fallow health --group-byselects groups and tracks a trend per group.--group <KEY>keeps only the matching groups. It accepts exact keys, globs and!negations. Each group gets severity and hotspot counts.--save-snapshoton a grouped run stores the group data (snapshot schema v11).--trendor the new--trend-from <PATH>then adds a trend per group. Markdown and the GitHub job summary get a## Health by <mode>table and one findings block per group.--top Nnow applies to each group. The MCPcheck_healthtool acceptsgroupandtrend_from.fallow report --from <file> --format markdownrenders saved JSON output. A saveddead-code,dupes,healthor bare combined run gives the same markdown as the live run with the same flags. You can analyze once and write both a job summary and a markdown report from one JSON file. The saved output of a combined run does not record--group-by, so its markdown is not grouped. A savedaudit,securityorfixrun, and a combined run with type-aware evidence, exit 2 with a reason.fallow dupesmarks symlinked clone instances. A clone instance under a symlink now hasis_symlink: truein JSON,symlink=truein compact output and a(symlink)marker in human output. To report only duplication between real files, setduplicates.ignoreSymlinks: trueor pass--ignore-symlinks.--no-ignore-symlinksoverrides the config value. The MCP tools, the Node API, the LSP and the VS Code settingfallow.duplication.ignoreSymlinksaccept the same option. Thanks @lzear for the report (#2961).fallow agent installadds a native commit gate for Codex. The install writes a PreToolUse handler to.codex/hooks.jsonand the gate script to.codex/hooks/fallow-gate.sh. The handler runs the audit from the nearest install root, also from a subdirectory. The Claude Code handler now also finds the nearest install root. Runfallow agent installagain to get the new handler. Codex runs a hook only after you trust it in/hooks.fallow agent statusshows the gate as its own row, andfallow hooks status --format jsonadds acodex_gateentry. TheAGENTS.mdblock is now routing guidance.fallow hooks install --target agent --agent codex --usernow writes the user gate and no longer writes the projectAGENTS.mdblock.- The new
fallow-setupskill tells an agent how to add code-quality tools. The skill is for a JavaScript or TypeScript project. It tells the agent to detect the existing tools, runfallow recommend, install Fallow as a dev dependency and add a CI gate with a baseline. It gives each tool one job, and it removes a tool only after a parity check.fallow agent installwrites it next to thefallowskill. - The
gdp-proof-producerrule-pack kind restricts proof producers. The opt-in rule restricts@gdp-ts/core.defineProofcalls toallowedFiles.proofKindsassigns literal proof labels to owner modules. The analysis follows static imports and unambiguous re-export chains. - Fallow has new built-in plugins.
release-itkeeps each.release-it.*config form and reads thepackage.jsonkey. It does not read the JSON5, YAML and TOML forms. It credits thepluginsandextendspackages.evemakes agent modules entry points.ag-uicredits the hooks of a class that extendsAbstractAgentdirectly.nestjs-trpccredits the methods that the module calls.kibanareadskibana.jsoncplugin manifests and adds their entry points.
- The health JSON names the sections that the run produced. The root array
sectionslists each section that the run computed. With--baseline,summary.baseline_staleness.remaining_findingsgives the number of functions above a threshold that the baseline does not accept. - The CLI, MCP and the LSP report the same
finding_idfor a security finding. The VS Code "Copy Fallow finding id" quick fix now works on a security candidate (#3035). - Fallow can suggest its Claude Code plugin. Inside a Claude Code session, Fallow writes one hint line to stderr for human output. Fallow writes the hint only without
--quiet, outside CI, and when no Fallow plugin or skill is installed. The hint has no effect until the official marketplace lists the Fallow plugin. SetFALLOW_CLAUDE_CODE_HINT=offorFALLOW_SUGGESTIONS=offto suppress it.
Changed
-
Tooling dependencies need evidence of use. Three kinds of devDependency no longer count as used by name alone:
- A command-line tool from the tooling catalogue.
- A tooling package of an active plugin.
- A
@types/Xpackage.
A tool counts as used when a script, a CI workflow or a git hook runs it. It also counts when its config file or its package.json key exists, or when a plugin credits it. Fallow now reads git hooks in
.husky/, lefthook, simple-git-hooks and lint-staged configs. A@types/Xpackage counts as used when the project declares or importsX, or namesXin tsconfigtypes. A short list of global type packages, such as@types/node, always counts.--trace-dependencynames each credit. Production dependencies still count as used by name. -
Root dependencies follow the nearest manifest. Each import now counts for the nearest manifest that installs the package. Fallow reports a root declaration as unused when each import uses a workspace declaration. The finding has a
declared_and_imported_infield that names those workspaces. Such an entry gets one finding, not a second test-only or type-only finding. -
export default Namelinks to the local bindingName. Fallow now reports unused members of a class that a file exports this way. WithignoreExportsUsedInFile, such a default export is used when the file usesName. -
fallow health --group-bycounts a clone in each group that holds an instance. The groupduplicated_linesvalues now sum to the project value. Group scores can drop once after the upgrade. -
CODEOWNERS matching follows GitHub. A pattern without a trailing
/owns the directory contents.*does not match/,apps/matches anappsdirectory at any depth, and/*owns only the files at the root. Owner groups can change. -
Gate rows show a count. The
health-findingsanderror-severity-findingsentries ingate_outcomesnow haveobservedandthreshold_label. The PR decision and the PR comment show the count, for example6 at or above error. -
fallow hooks install --target agentselects Codex from a.codex/directory, not fromAGENTS.mdalone.
Bug fixes
Dependencies
- Fallow no longer reports an optional peer of a used dependency as unused. Fallow also no longer reports a devDependency that the same manifest lists as a peer. An optional peer that the project does not list no longer credits its own peers.
- A peer-only entry no longer hides the ancestor workspace that installs a package. A workspace file can use a package that an ancestor manifest declares. This does not apply to production code in a publishable workspace. An ancestor declaration counts as used when a nested workspace uses it.
- A
postcss.config.*file credits the PostCSS plugins that it names, also without a declaredpostcsspackage. - A script binary credits every package that can provide it, for example
run-pfornpm-run-all2. - Fallow credits a runtime loader that a tool config needs, such as
ts-nodefor a TypeScript Jest config. The Mocha plugin reads.mocharcfiles and themochakey. - A tool config file in a workspace package credits the root devDependency of that tool.
require.resolveandimport.meta.resolvewith any subpath credit the package.require.resolve('pkg')reports an unlisted package. Thanks @DrJKL for the report (#3213).import {} from 'pkg', a module augmentationdeclare module 'pkg', and anode_modules/.bin/<name>path in source credit the package.- Fallow credits Expo config plugins in the app config, Oxlint
jsPluginsinsideoverrides, and packages in StarlightcustomCss. - React Router apps without
entry.serverno longer reportisbotas unused. - An esbuild call in a test file no longer removes the credit that a dependent workspace gets for the packages of a private sibling. Fallow removes that credit only when a build file or a package script sets every package external.
--trace-dependencygives the same credits as the dependency report. It names each manifest that the report flags.
Entry points and plugins
- The tsdown and tsup plugins read JSON configs, the
package.jsonkey and string or objectentryvalues. They also read config arrays, configs in workspace packages and tsdownworkspaceglobs. Thanks @moltar for the report (#3209). - Playwright reads every
defineConfigargument and the statictestDirandtestMatchsettings. - These files are now entry points: the test files of a bare
node --testscript, the tests of a vitest config that a script passes with--config, a file that a command substitution runs, and a file that a CI step runs with Bun. - The Vite
rootoption moves the default entries. An electron-vite config sets the main and preload entries. Default electron-builder script configs stay used. - Vercel functions in
api/are entry points, and the Vercel plugin also activates onvercel.json. Next.jsinstant,prefetchandunstable_dynamicStaleTimesegment exports are used. - Jest setup files that start with
<rootDir>resolve, also inprojects. - A config value or an alias replacement that calls a local path helper resolves.
- Quoted commands that
concurrentlyruns count as used. - A root
link:orfile:dependency on a local package makes that package a workspace.
Exports, types and class members
- A JSDoc
@importtag keeps the imported module used. - Fallow now reports an orphan module declaration file as an unused file.
- A type that shapes an exported
constis no longer an unused type. This covers a wrapper call, a type assertion, asatisfiesclause and an inferred factory return. Withprivate-type-leakson, a private type in a wrapper call, an assertion or an inferred return now gives a finding. - Fallow now reports an exported type that backs only unused exports.
- Fallow credits class members through a destructured field of an inline-typed parameter, an awaited async factory, a
??or||fallback, a nullable ternary, and aMapvalue. - Fallow credits the exports that code reads through dynamic imports: a member read on an awaited import, a
Promise.alldestructure, a local loader function, an assignment to an existing binding, andssrLoadModule. - A test spy call on a namespace import credits only the spied member.
Import resolution
- An import of
./ButtonreachesButton/index.tsbefore a siblingButton.cssorButton.vue. Thanks @osazemeu for the report and the initial fix (#3201, #3202). - A relative import or a package subpath with a missing gitignored target gives no
unresolved-importfinding. A file URL passed to a filesystem call also gives no such finding. - A relative import into a tsconfig
outDir, and a workspace export with a declaration target, resolve to the source file. - A
*in a packageexportstarget matches files in nested directories. - A
package.jsonentry under a missinglib/directory, such as./lib/index.mjs, maps back to the same-stem source file insrc/. A workspace import through anexportstarget underlib/also resolves to the source file. Thanks @moltar for the report (#3208). - Markup inside an inline
<script>body is no longer an HTML asset reference. - Fallow reads a loose
deno.jsonas Deno does. A malformed config in the tsconfigextendschain gives amalformed-tsconfigdiagnostic.
Health, duplication, security and CI
- The health score penalizes hotspots only when their score reaches 50 (formula version 3). Trends compare scores only when both formulas are known and equal. Thanks @PaulCailly for the report (#3203).
fallow health --save-snapshot --trendno longer compares a run with its own snapshot.- A grouped
--scorerun no longer shows group findings that the project section does not show. Job summaries with a baseline give the number of functions above a threshold. - A failed run prints one stderr line that names the gate that set exit code 1. In the bare run, the
type-aware-requireentry now fails when a section fails on type-aware completeness. - A bare human run with
--fail-on-regressionprints the regression outcome once. fallow dupes --fail-on-issuesand--ciexit 1 when clone groups are found. Thanks @TiagoGranelli for the report (#2984).overrides[].rulesapply to security candidates.fallow securitynow fails on a candidate only when the rule of that candidate iserror. Thanks @TiagoGranelli for the report (#2985).- The override and catalog checks follow the pnpm version. They skip
pnpm.overrideson pnpm 11 and later, andpnpm-workspace.yamloverrides where pnpm ignores them, and the newpnpm-workspace-overrides-ignoreddiagnostic says why. - A
catalog:value in a pnpm override counts as a catalog reference.unresolved-catalog-referencesreports an override that names a catalog without that package. Thanks @PrinceD96 for the report (#3078). unused-dependency-overridesreports no findings when the npm or pnpm lockfile does not parse and no other lockfile parses. A workspace diagnostic tells you to resolve the merge conflicts or to run the install again.
CLI and editors
- The editor Code Lens above an export shows "imported by N files". Thanks @danielo515 for the report (#3064).
fallow watchno longer stops on a change outside a nested.gitignoredirectory.- The VS Code extension downloads a musl binary on a musl Linux host.
fallow hooks statusandfallow agent statusshow paths with/on Windows.- A global flag before the subcommand no longer hides the
checkandreviewaliases.fallow securityandfallow similar-codereject every--dupes-*override. - The missing API key message names the cloud command that ran.
- The bundled skill references describe
fallow trace --path,fallow coverage analyze --debug-unmatchedand theignoreDependenciesglobs.
Performance
- YAML files parse faster. A 1.3 MB
pnpm-lock.yamlparses in 12 ms in place of 21 ms. A plain number in apnpm-workspace.yamloverride keeps its source text. Apnpm-workspace.yamlthat does not parse now reports themalformed-pnpm-workspace-yamldiagnostic.
Upgrade notes
-
The extraction and graph cache versions change, so the first run after the upgrade parses all files again.
-
These changes can report new unused dependencies:
- the tooling credit rules,
- the nearest-manifest rule for root dependencies,
- the
@types/Xrule.
Use
ignoreDependenciesfor a tool that the project runs in a way that Fallow does not see. -
A CI job that runs
fallow dupes --cinow fails when the project has clone groups. To keep a job that only reports, use--format sarif --quiet, or gate with--threshold. -
Group scores of
fallow health --group-bycan drop once. CODEOWNERS owner groups can change. -
The dead-code, audit, review-brief and combined schema versions advance, because the enum of policy rule kinds gains a value.
Full Changelog: v3.31.0...v3.32.0