Most repositories carry code nobody dares to delete, because deleting means proving a negative: fallow reads the repository as one dependency graph, from import edges to styling tokens, and reports what that graph shows.
Audit scope: 19 changed files vs HEAD~15 (8fbfcb054..HEAD)
● Unused files (2)
packages/vitest/src/public/reporters.ts
test/coverage-test/test/configuration-options.test-d.ts
● Circular dependencies (6)
packages/vitest/src/integrations/vi.ts
→ wait.ts → vi.ts
✗ dead code: 156 issues · complexity: 6 findings · duplication: 8 clone groups · 19 changed files (1.05s)
audit gate excluded 163 inherited findings (run with --gate all to enforce)
Excerpt from fallow audit on the vitest monorepo, auditing its last 15 commits. fallow 3.5.0, warm base-snapshot cache. The gate passed: the 163 inherited findings are pre-existing and excluded by design, so they do not block the change. The same run with --format json returns one typed JSON document.
Quick start
# Zero-install, full pipeline (dead code + duplication + health)
# Gate only what a PR changed
# Install as a devDependency
# For agents and scripts: exit 0 and 1 both mean the run succeeded (1 = findings);
# exit 2 is a real error, reported as a JSON envelope on stdout
The npm package ships the fallow, fallow-lsp, and fallow-mcp launchers plus a version-matched agent skill, so the editor and agent integrations resolve the project-local binary instead of whatever happens to be on PATH. Runs are deterministic: the same input produces the same output with stable fingerprints. Re-running to verify an edit is safe. Other channels (pnpm, yarn, cargo install fallow-cli, and a local Docker build with a Compose example at examples/docker/compose.yaml) are covered in the installation guide.
What fallow reports
- Unused files, exports, types, enum and class members, and dependencies
- Circular dependencies and re-export cycles, part of
fallow dead-code - Code duplication with a suffix-array detector covering JS/TS and CSS-family stylesheets, plus Vue/Svelte/Astro component regions
- Complexity hotspots and a 0 to 100 health score with a letter grade
- Architecture boundary violations with
bulletproof,layered,hexagonal, andfeature-slicedpresets - Design-system styling drift for CSS and CSS-in-JS
- A changed-file PR gate with a pass, warn, or fail verdict (
fallow audit) - Auto-fix with a dry-run preview
- Opt-in security candidates ranked by reachability from entry points (
fallow security)
Over 100 built-in framework plugins detect entry points automatically, so the first run needs no configuration. Fallow Runtime, the optional paid layer, merges production execution evidence into these same reports; see Runtime intelligence (optional) and static vs runtime.
Your first run
Findings on a first run usually mean fallow is missing an entry point or a framework convention, or is analyzing generated files you never meant to include. The built-in plugins take care of framework detection; it's generated code that usually needs a hint:
You do not have to author that config yourself. npx fallow recommend detects the stack (frameworks, workspace layout, test runner, package manager), prints a proposed config as a safe starting point, and ends with the few genuinely subjective choices it will not decide for you. It is read-only and always exits 0; nothing changes until you save the config.
Patterns are relative to the project root and add to fallow's built-in ignore defaults (node_modules, dist, coverage, minified bundles). Config precedence is first match wins per directory, with no merging: .fallowrc.json (JSONC accepted) > .fallowrc.jsonc > fallow.toml > .fallow.toml. The full reference is the configuration overview; known limits (syntactic analysis, no type resolution) are documented in limitations; for a hung or failed run, see debugging.
Adopting on an existing codebase? fallow audit fails only on findings a change introduces, so a legacy backlog does not block day one, and --save-baseline / --baseline quarantine the existing findings for the standalone commands. The adoption guide covers the staged path.
Commands
| Command | What it does |
|---|---|
npx fallow |
Full pipeline: dead code, duplication, health |
npx fallow audit |
Changed-file gate over dead code, complexity, duplication, and styling drift: verdict pass/warn/fail against a base ref. Fails only on findings the change introduced (--gate all widens) |
npx fallow dead-code |
Unused code and circular dependencies (alias: check) |
npx fallow dead-code --trace src/file.ts:symbol |
Prove a symbol is unused before deleting it |
npx fallow dupes |
Duplication; modes strict, mild (default), weak, semantic |
npx fallow health --score |
Complexity, 0 to 100 health score, hotspots; --css adds structural CSS analytics |
npx fallow fix --dry-run |
Preview auto-fixes; apply with npx fallow fix |
npx fallow guard src/file.ts |
Which boundary rules apply to a file before editing |
npx fallow security |
Opt-in security candidates; --gate new --changed-since <ref> fails only on introduced ones |
npx fallow explain <issue-type> |
Explain a rule without analyzing |
npx fallow recommend |
Detect the stack and propose a config; subjective choices stay open questions |
npx fallow init |
Scaffold config; --agents scaffolds an AGENTS.md |
npx fallow migrate |
Migrate from knip, jscpd, or stylelint config |
npx fallow schema |
Machine-readable capability manifest (always JSON) |
| Command | Purpose |
|---|---|
fallow review --brief |
Advisory orientation brief over changed files; always exits 0 |
fallow inspect --file src/api.ts |
Evidence bundle for one file, or one symbol via --symbol src/api.ts:client |
fallow trace src/utils.ts:formatDate |
Symbol-level call chains: callers up, callees down |
fallow watch |
Re-run analysis on file changes (interactive use; agents should not run it) |
fallow flags |
Detect feature-flag patterns |
fallow suppressions |
Inventory of fallow-ignore markers |
fallow list |
Entry points, files, plugins, and boundaries (--boundaries) |
fallow workspaces |
Monorepo workspace discovery diagnostics |
fallow config |
Resolved configuration and which file provided it |
fallow decision-surface |
Ranked structural decisions a change embeds |
fallow impact |
Opt-in, local-only report of what fallow caught; --all spans repos |
fallow report --from results.json |
Re-render a saved JSON result in another output format |
fallow ci ... |
PR/MR feedback helpers (comments, reviews, check runs) |
fallow ci-template gitlab --vendor |
Vendor the GitLab CI template for offline runners |
fallow hooks install --target git |
Managed pre-commit hook; --target agent writes agent-gate hooks |
fallow rule-pack init |
Declarative policy rule packs (list, test, schema) |
fallow plugin-check |
Dry-run an external framework plugin |
fallow config-schema |
JSON Schema for config; also plugin-schema and rule-pack schema |
fallow license activate --trial --email you@company.com |
Fallow Runtime licensing (status, refresh, deactivate) |
fallow telemetry status |
Opt-in telemetry, off by default (enable, disable, inspect --example) |
fallow coverage setup |
Runtime coverage workflow (analyze, upload-inventory, upload-source-maps, upload-static-findings) |
Per-command flags come from fallow schema (machine-readable) or the CLI reference.
Output and exit codes
For machine consumption, add --format json --quiet to any command and read the JSON on stdout. Exit 0 and 1 both mean the run succeeded (1 signals findings); exit 2 is a real error and still writes a JSON envelope to stdout. Branch on the code, treating 0 and 1 as success and 2 as failure, rather than blanket-suppressing with || true (which hides real errors from anything that checks the exit code).
--format |
What you get |
|---|---|
human (default) |
Terminal report with a Next: suggestion line |
json |
The machine contract: one typed JSON document on stdout |
sarif |
GitHub Code Scanning and other SARIF consumers |
compact |
One grep-friendly line per finding |
markdown (md accepted via FALLOW_FORMAT) |
Markdown report |
codeclimate (aliases gitlab-codequality, gitlab-code-quality) |
GitLab Code Quality report |
github-annotations |
Workflow-command annotations; render on fork PRs without a write token |
github-summary |
Job-summary markdown for $GITHUB_STEP_SUMMARY |
pr-comment-github, pr-comment-gitlab, review-github, review-gitlab |
Typed CI feedback envelopes for the bundled CI scripts |
badge |
shields.io-compatible SVG health badge; fallow health only (fallow health --format badge > badge.svg) |
human, json, sarif, compact, and markdown apply to every analysis command; the CI envelopes and badge belong to the command that produces them, as documented per format in the CI guide.
| Exit code | Meaning |
|---|---|
| 0 | Clean, or audit verdict pass or warn |
| 1 | Findings, or audit verdict fail (a normal outcome) |
| 2 | Validation or runtime error (JSON error envelope on stdout with --format json) |
| 7 | Network failure (license and cloud operations) |
| 8 | Security gate hit (fallow security --gate) |
Rule severity maps onto exit codes: error fails CI (the default), warn exits 0, off skips the rule (rules reference).
The JSON contract, in short:
- a root
kinddiscriminator names the analysis that produced the document - per-issue
actions[]with anauto_fixableflag, so a script knows which findings it can hand tofallow fix - root
next_steps[]suggestions are runnable as-is - errors arrive as
{"error": true, "message": "...", "exit_code": 2}on stdout, not as a stack trace
Typed contracts ship with the npm package: import type { CheckOutput, FallowJsonOutput } from "fallow/types". The generated schema lives at docs/output-schema.json. The output format is CLI-only, via --format or FALLOW_FORMAT, and is never set in config; all environment variables are listed in docs/environment-variables.md.
Built for agents
The JSON contract and exit codes above are the agent interface.
The MCP server covers analysis, audit, health, duplication, tracing, fix preview and apply, boundary guard checks, and target inspection; every tool documents its nearest CLI fallback, and a bounded read-only Code Mode sandbox composes analysis calls without filesystem or network access.
npx fallow recommend --format jsonis the onboarding entry point: it returns the detected stack, a proposed config, and every decision with its tier and rationale, and it ships the subjective choices as ready-to-ask questions with options and tradeoffs. An agent authors.fallowrc.jsonfrom evidence and asks the user only what fallow will not decide- A version-matched agent skill ships in the npm package under
node_modules/fallow/skills/fallow(agent skills, companion repo fallow-skills) npx fallow init --agentsscaffolds an AGENTS.md with a task-to-command matrixnpx fallow hooks install --target agentgatesgit commitandgit pushonfallow audit(hooks)- A compliance loop with a copy-paste agent prompt: docs/fallow-compliance.md
- To verify security candidates from an agent harness, follow docs/security-agent-verification.md
- Never run
fallow watchin an agent loop; it does not exit. Telemetry is off by default and opt-in only, withDO_NOT_TRACKhonored (docs/telemetry.md)
Suppressing findings
// fallow-ignore-next-line unused-export -- kept for plugin consumers
export const keepThis = 1;
// fallow-ignore-file <issue-type> suppresses the whole file. Both marker forms take a comma-separated list of issue kinds and an optional -- <reason> suffix that suppression hygiene records. JSDoc visibility tags (@public, @internal, @expected-unused -- <reason>) keep intentional library API surface quiet. fallow suppressions prints the inventory.
For staged adoption, save a baseline once from a clean ref with --save-baseline, then pass --baseline on every run; fallow audit accepts per-analysis baselines. Full syntax lives at suppression.
CI
GitHub Actions:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: fallow-rs/fallow@v3
The Action defaults to the full pipeline with PR-scoped analysis via automatic base detection, and it is a blocking gate out of the box: fail-on-issues defaults to true, so any finding fails the job. For a staged rollout, start report-only with fail-on-issues: false, or use command: audit so only findings a PR introduces can fail CI. A SARIF file is generated by default but uploading it to GitHub Code Scanning is opt-in (sarif: true plus permissions: security-events: write); inline annotations render without any of that. The @v3 tag floats within major version 3; pin an exact tag when the fleet needs reproducible runs. Sticky comments and review comments are inputs documented in the CI guide.
GitLab:
include:
- remote: 'https://raw.githubusercontent.com/fallow-rs/fallow/v3.5.1/ci/gitlab-ci.yml'
fallow:
extends: .fallow
MR comments need a GITLAB_TOKEN with api scope; CI_JOB_TOKEN cannot create notes. Runners that cannot reach GitHub raw can vendor the template with npx fallow ci-template gitlab --vendor.
CI runs diff-scoped on pull requests by default, so CI output can legitimately differ from a full local run. Commit baseline files to keep CI and local in agreement.
Runtime intelligence (optional)
Fallow Runtime is the optional paid layer. It merges production execution evidence (V8 coverage dumps via NODE_V8_COVERAGE, or Istanbul files) into fallow health and fallow audit: hot paths for careful review, cold-code deletion confidence, runtime-weighted health, and stale-flag evidence. A single local coverage capture is free; continuous and cloud runtime monitoring requires a license. Everything else in this README is free and needs no license.
Details: runtime coverage and static vs runtime.
Editors and integrations
- VS Code extension
- Zed and Neovim setups under the
editors/tree (Neovim guide) - The
fallow-lspserver: diagnostics, hover, code actions, and code lenses. It resolves the project-local binary from a devDependency install - The Node API
@fallow-cli/fallow-nodeexportsdetectDeadCode,detectDuplication, andcomputeHealth - README badges:
fallow health --format badge > badge.svg
Migrating from other tools
npx fallow migrate translates existing knip, jscpd, or stylelint configuration into a fallow config. Guides: from knip, from jscpd, and the comparison page.
Performance
On the dead-code benchmark set, fallow analyzes fastify in 64ms where knip 6 takes 205ms, and preact in 74ms against 2.01s (27.1x). The counterweight is real too: knip measures faster on astro and TypeScript, and jscpd remains faster at raw duplication scanning. fallow also completes analysis on three projects in the set where knip's runs errored on those projects' own config files (next.js at 20,558 files, vite, and vue/core).
Measured on fallow 2.100.0 (the most recent full benchmark capture), Apple M5, medians of 5 cold runs. Methodology, full tables, and reproduction scripts live in BENCHMARKS.md and benchmarks/; rerun them against any version. For how fallow relates to lint tooling, see fallow vs linters.
Documentation
Everything lives at docs.fallow.tools: quickstart, configuration overview, CLI reference, MCP, CI, and limitations. A machine-readable index is at docs.fallow.tools/llms.txt.
In this repository:
- docs/architecture-invariants.md: the invariants the analyzer holds itself to
- docs/plugin-authoring.md: writing an external framework plugin
- BENCHMARKS.md: benchmark methodology and reference tables
- ROADMAP.md: where fallow is headed
- CONTEXT.md: domain vocabulary for contributors and agents
Contributing and license
Missing a framework plugin? Found a false positive? Open an issue. Development setup is covered in CONTRIBUTING.md.
MIT, see LICENSE.