Skip to content

Releases: fallow-rs/fallow

v2.86.0: fallow security candidate catalogue + JSON kind discriminator

02 Jun 11:02
v2.86.0
1c8319d

Choose a tag to compare

Highlights

fallow security: a local security-candidate layer for agent verification

fallow security is an opt-in command that surfaces local, deterministic security candidates (not verified vulnerabilities) for a human or coding agent to verify. fallow ships the signal; your agent confirms and fixes.

This release expands it from one rule to a data-driven catalogue spanning 9 CWE categories:

  • dangerous HTML (CWE-79), OS command injection (CWE-78), code injection (CWE-94), SQL injection (CWE-89), SSRF (CWE-918), path traversal (CWE-22), open redirect (CWE-601), runtime-selectable crypto algorithm (CWE-327), unsafe deserialization (CWE-502)
  • Plus the original client-server-leak rule: a "use client" file that transitively reaches a non-public process.env secret, with a structural import-hop trace.

Design choices that keep the signal honest:

  • Conservative by default. A candidate fires only when the relevant argument is non-literal; a fully literal value (el.innerHTML = "<b>x</b>", child_process.exec("ls")) is never flagged. fallow prefers false-negatives over false-positives.
  • Parameterized SQL is not flagged. Tagged-template sql`${x}` and the object form .execute({ sql, args }) bind safely and do not fire; only string concatenation, interpolated template literals into .query() / .execute(), and sql.raw(...) do.
  • Node sinks are provenance-gated to their import source (command injection to node:child_process, vm code injection to node:vm, path traversal to node:path, crypto to node:crypto, deserialization to js-yaml / node-serialize).
  • Build configs and test files are excluded from candidate generation.
  • Opt-in and isolated. The rules default to off, never appear under bare fallow or the audit gate, and fallow security is the only surface. Output is human, JSON, or SARIF (SARIF emits at note level with per-category rule IDs and CWE tags for the GitHub Security tab).
  • Blind spots are counted in-band, so an empty result with a non-zero unresolved count is never a clean bill.

Adding a CWE category is a single catalogue entry with no code churn. Scope which categories run with security.categories include / exclude lists, and suppress a file with // fallow-ignore-file security-sink.

Changed

  • JSON envelope outputs now carry a top-level kind discriminator (dead-code, health, dupes, combined, audit, security, ...), so consumers identify the shape by kind instead of field-presence heuristics. schema_version is bumped; --legacy-envelope keeps the previous root shape for one migration cycle. (Closes #413.)

Fixed

Detection and integration fixes across this cycle:

  • A bare "@" plugin alias no longer swallows @scope/* npm packages (#838).
  • declare ambient class properties are no longer reported as unused class members (#839).
  • new URL('./dir', import.meta.url) directory targets no longer surface as unresolved imports (#840).
  • Quoted globs in package.json scripts are registered as entry points (#841).
  • NestJS lifecycle and handler methods are credited as used (#843).
  • A method called on an instanceof-narrowed value is credited as a use of that class's member (#845).
  • Oxlint config packages referenced through extends are credited (#846).
  • Bun bunfig.toml preload files are tracked, and the Bun plugin activates on @types/bun (#847).
  • duplicate-export no longer over-reports across unrelated packages (#848).
  • Pinia stores auto-imported by @pinia/nuxt are tracked (#740).
  • Nuxt composables and utils referenced only through auto-imports are tracked (#739).
  • resolve.alias shared via an imported identifier or built with spreads is no longer ignored. Thanks @michaljuris for the report (#811).
  • Svelte markup <script src> tags no longer surface as unresolved imports. Thanks @codingthat for the report (#835).
  • The GitHub Action now uploads SARIF on public repositories. Thanks @cloud-walker for the detailed report (#817).
  • The GitHub Action no longer logs a spurious SARIF generation failed warning on repos with issues (#813).
  • Two built-in plugins sharing a config file no longer emit an un-actionable collision warning (#808).
  • fallow health surfaces CRAP coverage-source consistency in JSON and adds a tunable secondary refactor band (#474).

Full Changelog: v2.85.0...v2.86.0

v2.85.0: health as a CI gate, broader framework detection, and fallow impact

30 May 21:12
v2.85.0
71cefac

Choose a tag to compare

v2.85.0: health as a CI gate, broader framework detection, and the new fallow impact report

A large release that lands the accumulated work since v2.84.0. The headline changes for everyday use: fallow health can now gate CI on a score, and framework/plugin detection got broader and more accurate. On top of that, a new local fallow impact report, opt-in telemetry, and a coverage-intelligence verdict for Fallow Runtime users.

fallow health as a CI gate

fallow health can now act as a real CI gate (#790):

  • --min-score N is now authoritative: the build fails when the health score drops below N, and complexity findings become informational rather than hard failures. (Previously --min-score did not take effect; if you were already passing it, it now does what it says.)
  • --report-only runs the full health output and always exits 0, for teams that want the signal without gating yet.
  • No gate flag behaves as before (any finding fails), so existing pipelines are unchanged unless you opt into the flags above.
  • The churn-hotspot window now shows in the metrics line and the markdown vital-signs section (#799).
fallow health --min-score 80      # fail CI below 80
fallow health --report-only       # always exit 0, just report

New and improved framework detection

The broadest free win this cycle is fewer false positives across more frameworks:

  • New Velite plugin (#774).
  • rspress: the @theme virtual module is credited (#787).
  • SvelteKit: layout-reset route filenames (+page@.svelte, +layout@named.svelte) are recognized as entry points (#797).
  • Nuxt: @nuxt/content content.config.ts is credited as a default-export entry (#801).
  • ESLint: meta-preset plugins pulled in via peerDependencies (e.g. @antfu/eslint-config) are credited as used (#805).
  • oxlint: CLI tooling packages such as oxlint-tsgolint are credited as used (#802).
  • React Compiler: babel-plugin-react-compiler is credited via reactCompilerPreset() (#764).
  • Danger: the Danger plugin activates from dangerfiles.

Plus correctness fixes that remove false "unused" reports:

  • Class members used via typed destructure bindings are credited (#762).
  • Workspace imports resolve when tsconfig paths point at an unbuilt dist (#763).
  • A binary invoked as bun --flag <bin> in a package script is credited (#761).
  • Workspace class members and SCSS include paths survive plugin-result merging (#783).

fallow impact (new, free, local)

fallow impact is an opt-in, local report that shows whether your codebase is trending cleaner over time. It is off by default, writes a single gitignored file at .fallow/impact.json, and never affects exit codes.

fallow impact enable     # start recording (off by default)
fallow impact            # show the report

It surfaces three things:

  • Now: how many findings the latest run reported (#788).
  • Trend: the change in finding count versus the previous run (#788).
  • Resolved: which findings you actually fixed between runs, distinguished from ones you suppressed or moved (#803).
  • Pre-commit saves: how often a fallow audit pre-commit gate blocked a bad change until it was fixed (#788).
  • Whole-project track: the report can be credited from full fallow runs, including duplication (#812).
  • A read-only impact MCP tool lets agents query the report without enabling or disabling it (#804).

The store is gitignored on enable and unsupported formats are rejected up front (#795).

Duplication

  • CRLF-invariant clone fingerprints (#809): a clone's fingerprint is now stable across platforms regardless of line endings.
  • Forward-slash path normalization in human output on Windows (#807).
  • Trace a clone family by fingerprint: drill into a specific clone group via its fingerprint, with hardened deep-dive handles (#769).

CLI and source evidence

  • fallow upload-static-findings (#796): uploads findings for the source-evidence viewer.
  • Repo-relative source-map paths (#810): each source map's repo-relative path is uploaded so monorepo source resolution works.

Configuration

  • Multiple-config warning (#780): fallow warns when more than one config file coexists in a single directory, so which config is active is never a surprise.
  • Configuration hint on empty fallow flags output (#785): an empty flags result now points you at how to configure flag detection.

Opt-in telemetry

Optional, coarse usage telemetry to help prioritize framework and feature work. It is off by default, never collects paths, names, source, or config, and has fleet-wide kill switches (#770):

fallow telemetry enable      # opt in
fallow telemetry inspect     # print the exact payload, send nothing
# DO_NOT_TRACK=1 or FALLOW_TELEMETRY_DISABLED=1 hard-disables it

Coverage intelligence (Fallow Runtime)

For Fallow Runtime users with coverage data, fallow health gains a coverage-intelligence verdict that weighs static findings against runtime coverage evidence (#768). Requires Fallow Runtime and a coverage source.

Supply-chain and self-hardening

Internal hardening of fallow's own build and analysis path. No action needed for users:

  • Analysis only ever shells out to git, nothing else (#773).
  • Guards against hidden-unicode and agent-file poisoning, with a CI gate on baseline drift (#779, #794).
  • --ignore-scripts on dev-dependency and release-pipeline installs (#775, #782).
  • A documented signing-key rotation and compromise-response runbook (#781).

Thanks

Reporters who surfaced detection gaps fixed in this release:

Full Changelog: v2.84.0...v2.85.0

v2.84.0: @sanity/pkg-utils support

28 May 11:42
v2.84.0
1fe7ae6

Choose a tag to compare

@sanity/pkg-utils support

Projects built with @sanity/pkg-utils no longer need to hand-list their build configs in entry.

A new pkg-utils plugin keeps package.config.{ts,js,mts,mjs,cts,cjs} and package.bundle.{ts,js,mts,mjs,cts,cjs} reachable automatically, at the repo root and in every workspace package. The tool discovers these files by filename rather than importing them from source, so they previously surfaced as unused-file and had to be listed manually.

The plugin activates only on an exact @sanity/pkg-utils dependency, so a plain @sanity/client consumer with a stray package.config.ts keeps reporting it. It also credits @sanity/pkg-utils as a tooling dependency. The credit is scoped to the two build-config filenames, so an unreferenced ordinary source file still reports.

pkg-utils monorepos can drop the package.config.ts / package.bundle.ts lines from their entry config entirely.

Full Changelog: v2.83.0...v2.84.0

v2.83.0: line-move-tolerant runtime-coverage baselines, Nuxt auto-imports, more framework plugins

27 May 14:25
v2.83.0
8c56182

Choose a tag to compare

Runtime coverage

  • Cross-surface function identity. Runtime coverage now adopts the fallow-cov-protocol v2 FunctionIdentity (a fallow:fn:<hash> join key over file, name, and start line). One value identifies a function across findings, hot paths, blast-radius, and importance entries, and across V8 / Istanbul / oxc producers (columns are excluded from the hash). Old artifacts without an identity keep working via the existing path/name/line fallback. (Closes #506)
  • Baselines survive line moves. Function identities can now carry a source_hash: a content digest of the function body that excludes position. Runtime-coverage baselines match on it in addition to stable_id and the legacy id, so a function that moves to a different line but is otherwise unedited stays suppressed; editing the body re-surfaces it for review. (Closes #742)

Framework support

  • Nuxt components referenced only by template tag are now tracked. A new auto_imports plugin capability synthesizes a real graph edge from <MyComponent />-style tags to their component files (directory-prefixed PascalCase, <Lazy...>, .client/.server/.global). Additive by default; opt into autoImports: true to report genuinely-unreferenced convention components as unused. Thanks @Hal-Spidernight for the detailed proposal. (Closes #704)
  • New built-in plugins: Contentlayer, Obsidian, WXT, Varlock, OpenNext Cloudflare, and browser-extension (WebExtension / Chrome MV2+MV3 manifests) projects no longer report their config files, entrypoints, and manifest-declared runtime files as unused.

Fixes

  • ignoreUnresolvedImports config field accepts import-specifier globs to suppress expected unresolved-import findings. Thanks @OmerGronich. (Closes #726)
  • tsdown .mts / .cts config files are treated as entry points. Thanks @eojoel. (Closes #744)
  • fallow audit no longer emits spurious Nuxt/Astro prerequisite warnings on its base pass (#705).
  • Workspace packages without an exports map resolve missing prebuild output back to source. (Closes #725)
  • File health scores surface high CRAP-risk files even when their Maintainability Index is not the lowest. (Closes #554)
  • POSIX-rooted paths from Linux tooling stay absolute on Windows. (Closes #614)
  • GitHub Action: artifacts-dir writes generated files outside the repository root. Thanks @Guria. (Closes #735)

Notes

stable_id values use the reconciled cross-producer recipe shared by the CLI, the fallow-cov sidecar, and cloud aggregation. Runtime-coverage baselines written on a previous version re-baseline once.

Full Changelog: v2.82.0...v2.83.0

v2.82.0: framework plugins, quieter output, safer fixes

26 May 15:54
v2.82.0
c9d5682

Choose a tag to compare

Highlights

A broad framework-plugin release: ng-packagr Angular libraries, electron-vite, Playwright webServer, SvelteKit remote functions, k6, Supabase Edge Functions, Mintlify, and Iconify icon packages all stop producing false unused findings out of the box. Plus quieter JSON output, smarter fallow fix, and a binary-verification model that no longer relies on npm install scripts.

Features

Framework plugins

  • Angular ng-packagr libraries: lib.entryFile from ng-package.json / ng-package.prod.json (default src/public_api.ts) is now a package entry point, resolved relative to the config directory, so the library public API and everything reachable through its re-export chain stays alive instead of reporting as unused. Nested secondary-entry-point configs (packages/lib/client/ng-package.json) are scanned too, and the plugin also activates on a ng-packagr dependency. (#606)
  • electron-vite: renderer, preload, and main entries declared in electron.vite.config.* build.rollupOptions.input (string, array, and object forms, including resolve(__dirname, ...) path helpers) are treated as entry points. (#600)
  • Playwright webServer.command: CLI dependencies and local server scripts launched by webServer.command (object and array forms) are credited and kept reachable, with paths resolved relative to the config directory. (#621)
  • Vitest test.alias / resolve.alias: now recognized in vite.config.*, in test.projects[*], and in vitest.workspace.* files, covering mock and virtual-module aliases so they stop reporting as unresolved imports, unlisted dependencies, or unused exports. (#601)
  • SvelteKit remote functions: src/**/*.remote.{ts,js} files and their exported query / command / form / prerender functions are kept reachable. (#611)
  • k6: load-test scripts (*.k6.{js,ts,...}) act as runtime entry surfaces, the k6 CLI is credited, and the k6 / k6/* runtime namespace is recognized. (#625)
  • Supabase Edge Functions: Deno jsr: / npm: / URL imports resolve correctly, and supabase/functions/*/index.* files are runtime entry roots. (#624)
  • Mintlify: docs.json / mint.json, the mint / mintlify CLI, and MDX content under the docs root are recognized via a new built-in plugin. (#626)
  • Iconify: @iconify-json/<prefix> packages used only through static icon strings (<Icon name="jam:github" />) are credited as referenced. Thanks @nicolas-deyros for the report. (#608)

Bug fixes

  • Vite path-helper entries: build.rollupOptions.input / build.lib.entry written as resolve(__dirname, ...) / path.resolve(...) / join(...) / import.meta.dirname calls are evaluated to entry patterns; the shared extractor also benefits Webpack, Rspack, Rsbuild, Rolldown, Rollup, Vitest, and Drizzle. (#604)
  • Error subclass name: a name member on a class whose heritage reaches a native error constructor (Error, TypeError, ...) is treated as runtime-used rather than an unused class member. (#620)
  • new Class().method() receivers: methods called on a freshly-constructed instance, directly or through a self-returning fluent chain, are credited. (#605)
  • fallow fix low-confidence exports: export removals are withheld for files under off-graph consumer directories (__mocks__, e2e, fixtures, ...) and files with unresolved imports, so a fix never turns an analysis false positive into a broken build. A skipped_low_confidence_exports count and per-entry skip_reason are added to JSON output. (#602)
  • Quieter JSON / CI output: repeated workspace and plugin diagnostics on stderr are aggregated into one summary line per glob pattern or framework, instead of one line per directory. The structured workspace_diagnostics[] array is unchanged. (#637)

Changed

  • Binary verification moved from npm install's postinstall hook to first-run inside fallow, fallow-lsp, and fallow-mcp. The Ed25519 signature + SHA-256 digest check is preserved bit-for-bit (same key, same fail-closed behavior); a sentinel caches the verified state per install dir + binary hash. This removes the dependency on npm install scripts ahead of npm RFC 868. fallow --version now prints a verified: yes line; FALLOW_SKIP_BINARY_VERIFY=1 warns once per invocation when active, and FALLOW_VERIFY_CACHE_DIR redirects the sentinel for read-only install dirs.
  • --performance annotations: the parse/extract line gains a (parallel: ~Nms CPU) suffix, reused health stages read (measured above), and both breakdowns gain an (other) row so stages reconcile with TOTAL. JSON output gains observational parse_cpu_ms / shared_parse fields. (#481)

v2.81.0: OpenCode, RedwoodSDK, Wuchale, and Lexical plugins plus reliability hardening

26 May 04:49
v2.81.0
9de44d7

Choose a tag to compare

Framework support

  • OpenCode: .opencode/plugins/* modules, @opencode-ai/plugin, and npm plugins declared in opencode.json are recognized as loaded, so they no longer report as unused files or dependencies. (Closes #629)
  • RedwoodSDK: src/worker.{ts,tsx,js,jsx,mts,mjs} and files reached through the rwsdk/vite worker stay reachable without broad dynamic-load suppressions. (Closes #632)
  • Wuchale: wuchale.config.js, adapter packages imported only from that config, and static Vite configFile references are kept reachable in active Wuchale projects. (Closes #631)
  • Lexical: classes extending DecoratorNode, ElementNode, or TextNode no longer report their framework-invoked lifecycle methods (getType, clone, createDOM, updateDOM, decorate, and the serialization/DOM set) as unused class members. (Closes #628)

Detection fixes

  • node:sqlite and other mandatory-node:-prefix builtins (node:sea, node:test, node:test/reporters) are recognized as platform builtins when written with the node: prefix, so they no longer surface as unlisted dependencies or unresolved imports. Bare forms remain ordinary npm packages. (Closes #627)
  • Scaffold template package assets such as template-*, templates/**, and scaffolds/* published through package.json#files are treated as support entry points, including in monorepo workspaces. (Closes #635)
  • Oxlint jsPlugins packages loaded only through jsPlugins (for example eslint-plugin-testing-library, eslint-plugin-playwright) are credited as referenced dependencies instead of reporting as unused. Thanks @pasTa4667 for the patch. (Closes #607)
  • Re-export source edges now participate in resolver diagnostics: missing export { x } from "./missing" sources report as unresolved imports, and package re-exports report their actual source line. Thanks @M-Hassan-Raza for the patch. (PR #666)

Reliability and integrations

  • fallow ci reconcile-review applies stale review cleanup fail-fast through a typed plan, preflights provider targets before mutation, and reports apply_hint, failed_fingerprints, and unapplied_fingerprints. (Closes #459)
  • fallow watch debounces directly and recovers when the watched root is moved, deleted, or recreated, applying the same source/config/ignore filters as discovery. (Closes #456)
  • fallow coverage setup resumes safely after interruption, persisting step progress in .fallow/setup.json, guarding concurrent runs with .fallow/setup.lock, and writing state atomically. JSON planning mode stays read-only. (Closes #460)
  • Ownership-aware health hotspots emit a required ownership_state discriminator (active, unowned, declared_inactive, drifting), suppress vague drift only for matched active owners, and add collision-safe author handles. The new --ownership-emails anonymized spelling is accepted alongside hash. (Closes #478)
  • Programmatic and Node analyses keep per-call thread and diff scope: diff_file (Rust) / diffFile (N-API) per call, each building its own Rayon pool. (Closes #469)
  • Fallow cloud API calls honor Retry-After backoff (capped at 60s), parse error envelopes consistently, accept a custom trust bundle via FALLOW_CA_BUNDLE, and exit 7 when setup or transport failures prevent every upload. (Closes #464)

Install

npm install -D fallow@2.81.0
# or
cargo install fallow-cli@2.81.0

Full Changelog: v2.80.0...v2.81.0

v2.80.0: optional review guidance, summary scope, Fumadocs plugin

24 May 07:39
v2.80.0
017658e

Choose a tag to compare

Highlights

This release adds optional inline review guidance and a diff-scoped sticky summary mode for CI integrations, ships a built-in Fumadocs plugin, and lands a batch of false-positive fixes for Wrangler precedence, TanStack Start virtual modules, MDX code fences, Node script runners, Bun's bare runtime module, prebuild package maps, and React Router type imports.

Added

  • Inline review comments can include optional rule guidance. Set FALLOW_REVIEW_GUIDANCE=true (or GitHub Action review-guidance: true) to append collapsed "What to do" blocks to review-github and review-gitlab comments using the existing rule guides from fallow explain. Default stays off. fallow/unused-type review comments also pick up the same one-line export-stripping suggestions as unused-export. Thanks @OmerGronich for the request. (Closes #659)

  • Sticky PR/MR summary comments can scope project-level findings to the diff. Set FALLOW_SUMMARY_SCOPE=diff (or GitHub Action summary-scope: diff) to apply the diff filter to dependency, catalog, and override findings in pr-comment-github and pr-comment-gitlab. The default all preserves the existing behavior where sticky summaries include those findings even when their fixed package.json or workspace-manifest anchor line is outside the diff. Inline review comments are unaffected. Thanks @OmerGronich for the request. (Closes #661)

Fixed

  • Fumadocs MDX projects no longer report configured docs content as unused. A built-in Fumadocs plugin activates from fumadocs-mdx, fumadocs-core, fumadocs-ui, or source.config.*, keeps source.config.* and .source/**/*.{ts,tsx,js,jsx,...} reachable, suppresses fumadocs-mdx:* virtual imports, credits packages imported by the source config, and extracts literal dir values from defineCollections, defineDocs, and direct defineConfig({ collections }) entries as MDX content roots. (Closes #633)

  • Wrangler config precedence now matches Wrangler's selected config file. Projects with multiple sibling wrangler.* config files previously credited every main value as an entry, so stale migration leftovers (e.g. wrangler.toml) could keep dead worker files alive. Fallow now only reads main from the highest-precedence sibling: wrangler.json, then wrangler.jsonc, then wrangler.toml. All sibling configs themselves remain credited as used. (Closes #630)

  • TanStack Start :v virtual modules no longer surface as unlisted dependencies. Imports such as tanstack-start-manifest:v and tanstack-start-injected-head-scripts:v are now recognized as plugin-registered framework virtual modules, and skipped from unlisted-dependency and unresolved-import reporting when the TanStack plugin is active. (Closes #636)

  • Node package-script and forked runner entrypoints no longer report as unused. Package scripts such as node scripts/process-messages resolve extensionless directory paths to their index.* files, and statically resolvable local child_process.fork() targets from proven node:child_process imports are credited as dynamic entrypoints, including the fork(path.resolve(fileURLToPath(import.meta.url), '../runner.js')) shape. (Closes #638)

  • MDX documentation code fences no longer create unresolved imports. Fenced TypeScript examples in .mdx files were previously extracted like executable top-level statements, so docs snippets with virtual // file: boundaries reported false unresolved-import findings. Fenced code blocks are now skipped during MDX import/export extraction, while real top-level imports continue through the parser path. (Closes #639)

  • Workspace and self package imports that point at missing prebuild output now resolve back to source. Packages such as Nitro and Redux Toolkit could previously report false unresolved-imports, unlisted-dependencies, unused-dependencies, and unused-files when package.json imports or exports selected dist targets before a build had run. Fallow now uses the nearest package manifest for #... imports and known root/workspace package manifests for self or workspace specifiers, maps project-relative output targets back to tracked src candidates, and preserves dependency usage metadata when those imports resolve to internal source files. (Closes #641)

  • Bun's bare bun runtime module is no longer reported as an unlisted dependency. import { SQL } from "bun" and type-only imports from "bun" are recognized as a Bun runtime builtin, alongside the existing bun:* builtin recognition. Real packages such as bun-types, @types/bun, bunyan, and bun/* subpaths remain normal dependencies. (Closes #642)

  • React Router v7 and Remix generated ./+types/* route modules no longer surface as unresolved imports. Route modules using import type { Route } from "./+types/root" previously reported false-positive findings because those files are generated by the framework's typegen step and are often gitignored. The React Router and Remix plugins now declare ./+types/ as a generated type-import prefix; the suppression is plugin-gated and type-only, so runtime imports under the same prefix still report. (Closes #645)

  • Windows CI is green again. The Fumadocs integration test now normalizes path separators before asserting on unused_files, restoring the Windows leg of ci.yml. No user-visible behavior change.

Full Changelog: v2.79.0...v2.80.0

v2.79.0: Ember.js / Glimmer plugin, JSX + class-member + worker / loader false-positive fixes

22 May 21:30
v2.79.0
6d5b84c

Choose a tag to compare

Highlights

Ember.js / Glimmer / Embroider plugin. First-class support for strict-mode Ember apps and v2 addons: ember-source / ember-cli / @embroider/* packages stay credited, framework-invoked lifecycle methods on Component / Route / Controller / Service / Helper / Modifier / Application / Router subclasses are no longer flagged as unused class members, the @ember/* virtual-module paths exposed through the AMD loader and Embroider rewriter stop reporting as unresolved imports, and classic-layout filesystem conventions (app/components/**, app/routes/**, tests/**/*-test.{js,ts,gjs,gts}, ember-cli-build.js, etc.) are added as entry-point globs. .gts / .gjs single-file components are first-class: imports referenced inside <template>...</template> blocks (PascalCase tag invocation, mustache / triple-stash / sub-expression helpers, element modifiers, dotted refs) are credited; Handlebars built-ins (if, unless, each, let, yield), this.* chains, and @arg references are not. Thanks @mike-engel for the plugin (PR #369).

False-positive fixes across JSX, class members, Cloudflare Workers, Content Collections, Node loaders, and HTML scaffolding.

Added

  • Ember.js / Glimmer / Embroider plugin with strict-mode .gts / .gjs template-binding usage tracking. See above.

Fixed

  • JSX resource attributes no longer report as unresolved imports. Generic TSX metadata like <link rel="stylesheet" href="style-a.css" />, <link rel="modulepreload" href="/vendor.js" />, and <script src="./script-a.js" /> no longer emit synthetic side-effect imports; HTML files and bare html tagged-template asset scanning are unchanged. (Closes #640.)

  • Combined human summaries are less repetitive, and fallow explain accepts issue labels with spaces. fallow --summary no longer prints both a section header and the summary renderer's own title; the loaded config: notice is deduped per config file; fallow explain unused files and fallow explain code duplication work the same as the hyphenated spellings.

  • Public class members exposed through non-private package entry points are no longer reported as removable internals. Library packages that re-export builder or database classes from package.json entry points (main, root exports, or subpath exports) had every uncalled public method reported as unused-class-member. find_unused_members now treats classes reached from non-private entry-point re-exports (including the transitive export * closure and src/**/index.* source subpath indexes in packages without an exports map) as public API and skips class member findings for those exports. Enum members, private app packages, and internal reachable classes are unchanged. (Closes #643.)

  • Cloudflare Workers, Content Collections, and Node module.register() loaders no longer surface as false positives. Cloudflare Workers projects with "main": "src/worker.tsx" (or any env.<name>.main override) in wrangler.{toml,json,jsonc} keep that worker entry alive; the static glob widens to src/{index,worker}.{ts,tsx,js,jsx,mts,mjs}. Content Collections projects (@content-collections/{core,vite,next,solid-start,remix-vite,qwik,vinxi}) keep their root config alive and the integration packages stay credited. Node module.register('./hooks/loader.ts', import.meta.url) calls now credit the loader file's hook exports (initialize / resolve / load / globalPreload plus legacy getFormat / getSource / transformSource). Thanks @M-Hassan-Raza for the patch. (Closes #588, #589, #590.)

  • Playwright extend() fixture helpers are credited as used class members. Helper classes referenced only as Playwright fixtures (test.extend({ helper: async ({}, use) => use(new MyHelper(page)) })) no longer report every public method as unused-class-member. Playwright Page Object Model patterns no longer surface false-positive removable-internal findings.

  • HTML asset scanner skips build-time template-placeholder specifiers. <script src="{{rootURL}}assets/app.js"> (Ember's app/index.html), <script src="###APPNAME###/..."> (ember-cli blueprint scaffolds), and equivalent shapes from Handlebars / Mustache / Jinja2 / pre-compiled Vue or Angular templates are filtered at extraction time instead of being seeded as unresolvable specifiers. {{ and ### are never valid in a real <script src> / <link href> path, so the filter is generic across template engines.

Install

npm install -g fallow@2.79.0

or via Homebrew, cargo install fallow-cli, or the install script. See INSTALL.md for the full matrix.

Full Changelog: v2.78.1...v2.79.0

v2.78.1: npm postinstall no longer hits GitHub rate limits

22 May 17:06
v2.78.1
7f89de4

Choose a tag to compare

Patch release

Fixed

  • npm install fallow postinstall no longer fails on shared-IP CI runners with digest-unavailable. The postinstall verifier previously fetched each platform binary's expected SHA-256 from the unauthenticated GitHub release API. Pooled CI IPs (Buildkite, GitHub Actions shared runners, internal build clusters) routinely exceeded GitHub's 60 req/hr unauthenticated limit, and pnpm install --frozen-lockfile aborted with fallow: binary verification failed ... (digest-unavailable): GitHub release API returned HTTP 403: API rate limit exceeded. The release workflow's npm-prep job now computes the SHA-256 of every binary inside each @fallow-cli/<platform> package and writes it into the platform package's package.json under fallowDigests. verify-binary.js reads that embedded value first and only falls back to the GitHub API for older platform packages that lack the field, so steady-state installs perform zero network calls during digest verification. The Ed25519 signature layer and the FALLOW_SKIP_BINARY_VERIFY escape hatch are unchanged. (Closes #597. Thanks @drgnkpr for the report.)

  • Windows clippy on main is green again. Replaced an unfulfilled #[expect(dead_code)] annotation on ScopedChild::id with #[allow]. The function is pub inside a pub mod, so rustc never flags it as dead under -D warnings, and the previous expect annotation broke ci.yml's Windows leg. No user-visible behavior change.

Upgrade

npm install -g fallow@2.78.1

Full Changelog: v2.78.0...v2.78.1

v2.78.0: production CodeClimate fix, --explain everywhere, Windows hardening

22 May 12:11
v2.78.0
e5e082a

Choose a tag to compare

Highlights

Added

  • fallow flags covers 5 more SDKs by default: PostHog, Vercel Flags, ConfigCat, Optimizely, and Eppo. No config required; user-authored flags.sdkPatterns still layer on top. (Closes #563)
  • --explain now works on every invocation shape, not just subcommand+JSON. Human output gains a Description: line under each rule / metric. Combined-mode JSON gains a top-level _meta block aggregating the per-analysis metadata so fallow --explain --format json is a single-call discovery surface for rule docs. (Closes #559)
  • License clock-skew defense: fallow-license now rejects JWTs whose iat is more than 24h in the future (a forward-signed token, or a clock-behind-reality runner accepting a long-expired license). New LicenseError::ClockSkew variant, tolerance configurable via FALLOW_LICENSE_SKEW_TOLERANCE_SECONDS env var, default 24h matching jsonwebtoken / pyjwt / jjwt conventions. (Closes #453)

Changed

  • One persistent progress spinner instead of per-stage flicker. Before, each pipeline stage (discovery, extract, graph, analyze, dupes, health) had its own spinner; on fast machines none of them painted, so fallow on a moderately-sized project appeared frozen between command issue and final summary. Now a single spinner spawns at the start of the run, updates the stage label in place, and respects FALLOW_QUIET=1 / --quiet / non-TTY stderr. (Closes #560)

Fixed

  • fallow --score and fallow --trend now actually render the health score and trend in the terminal in combined mode. Previously the values were computed and emitted to JSON / SARIF / CodeClimate but the human renderer silently skipped them. (Closes #557)
  • fallow check --format codeclimate --production no longer panics with internal error: entered unreachable code when a --production-suppressed dep / export / member rule resolves to Severity::Off. Generic CodeClimate helpers now map severity lazily inside the loop body instead of eagerly before iteration. (Closes #452)
  • Workspace-relative paths instead of bare basenames in the nudge line, refactoring-targets nudge, Angular rollup line, and CRAP-coverage (inherited from ...) suffix. Nx / Angular / Rust monorepos with many same-named files (index.ts, mod.rs, *.component.ts, template.html) now show enough path context to identify which file fallow is pointing at. (Closes #547)
  • CSS Modules unused-export findings now report the real source line and column instead of line: 1, col: 0. LSP "go to definition" jumps to the actual class declaration; SARIF regions anchor to the right line; --diff-file line-overlap classification works correctly. (Closes #549)
  • CSS Modules nested cascade-layer sub-names (@layer foo.bar { ... }) no longer report as unused exports. Thanks @BowlingX for the report. (Closes #540)

Windows CI hardening

The push-to-main Windows CI matrix (added in #447) surfaced a class of long-latent Windows-only path-shape bugs that had been invisible because PR runs were ubuntu-only:

  • --changed-since and fallow audit now match findings on Windows instead of silently returning zero. Three sites moved from std::fs::canonicalize (which adds the \\?\ verbatim prefix on Windows) to dunce::canonicalize so canonical paths agree with opts.root shape. (Closes #561)
  • fallow audit --gate new-only no longer reports every inherited finding as freshly introduced on Windows; BASE-vs-HEAD finding-key intersection now strips the \\?\ prefix before strip_prefix. (Closes #561)
  • Diff-aware analyses (--diff-file, --diff-stdin, --changed-since) and the --file filter on fallow check now classify POSIX-style absolute paths (/foo/bar) correctly on Windows. New crates/cli/src/path_util::is_absolute_path_any_platform recognizes host-absolute, POSIX-root, and Windows-drive-prefix shapes regardless of host. (Closes #545)
  • Windows fallow binary now links with a 16 MiB main-thread stack (was Windows's 1 MiB default). Audit integration tests crashed with STATUS_STACK_OVERFLOW on a recursive code path inside fallow audit that exceeded 1 MiB but stayed under POSIX's 8 MiB default. (Closes #556)
  • The main Rust Check job now runs on Windows in addition to Linux on every push to main, catching this class of path-shape bug before the next release rather than at user-report time. PR runs stay ubuntu-only for fast feedback. (Closes #447)

Internal

  • Narrowed an import in crates/mcp/src/server/tests/run.rs so two Unix-only helpers are gated on #[cfg(unix)], silencing a Rust 1.95 unused-imports clippy regression on the windows-latest CI leg.

Install

npm install fallow@2.78.0
# or
cargo install fallow-cli --version 2.78.0
# or via Homebrew / direct binary download

Full Changelog: v2.77.0...v2.78.0