{"id":47636290,"url":"https://github.com/fallow-rs/fallow","last_synced_at":"2026-04-05T20:03:02.728Z","repository":{"id":345027322,"uuid":"1184090452","full_name":"fallow-rs/fallow","owner":"fallow-rs","description":"Find unused code, code duplication, circular dependencies, and complexity hotspots in TypeScript/JavaScript projects","archived":false,"fork":false,"pushed_at":"2026-03-26T12:32:54.000Z","size":8265,"stargazers_count":16,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-26T12:38:27.063Z","etag":null,"topics":["cli","code-duplication","code-quality","copy-paste-detection","dead-code","developer-tools","duplicate-code","javascript","jscpd","knip","linter","oxc","rust","static-analysis","typescript","unused-code","unused-dependencies","unused-exports"],"latest_commit_sha":null,"homepage":"https://docs.fallow.tools","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fallow-rs.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":"SECURITY.md","support":null,"governance":null,"roadmap":"ROADMAP.md","authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null},"funding":{"github":["bartwaardenburg"]}},"created_at":"2026-03-17T08:40:57.000Z","updated_at":"2026-03-26T12:32:02.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/fallow-rs/fallow","commit_stats":null,"previous_names":["bartwaardenburg/fallow","fallow-rs/fallow"],"tags_count":30,"template":false,"template_full_name":null,"purl":"pkg:github/fallow-rs/fallow","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fallow-rs%2Ffallow","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fallow-rs%2Ffallow/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fallow-rs%2Ffallow/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fallow-rs%2Ffallow/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fallow-rs","download_url":"https://codeload.github.com/fallow-rs/fallow/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fallow-rs%2Ffallow/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30999566,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-26T18:07:05.776Z","status":"ssl_error","status_checked_at":"2026-03-26T18:07:05.331Z","response_time":114,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cli","code-duplication","code-quality","copy-paste-detection","dead-code","developer-tools","duplicate-code","javascript","jscpd","knip","linter","oxc","rust","static-analysis","typescript","unused-code","unused-dependencies","unused-exports"],"created_at":"2026-04-02T00:09:27.826Z","updated_at":"2026-04-05T20:03:02.722Z","avatar_url":"https://github.com/fallow-rs.png","language":"Rust","readme":"\u003cp align=\"center\"\u003e\n  \u003cpicture\u003e\n    \u003csource media=\"(prefers-color-scheme: dark)\" srcset=\"https://raw.githubusercontent.com/fallow-rs/fallow/main/assets/logo-dark.svg\"\u003e\n    \u003csource media=\"(prefers-color-scheme: light)\" srcset=\"https://raw.githubusercontent.com/fallow-rs/fallow/main/assets/logo.svg\"\u003e\n    \u003cimg src=\"https://raw.githubusercontent.com/fallow-rs/fallow/main/assets/logo.svg\" alt=\"fallow\" width=\"290\"\u003e\n  \u003c/picture\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cstrong\u003eCodebase analyzer for TypeScript \u0026 JavaScript — unused code, duplication, complexity, and architecture.\u003c/strong\u003e\u003cbr\u003e\n  \u003cstrong\u003eRust-native. Zero config. Sub-second.\u003c/strong\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/fallow-rs/fallow/actions/workflows/ci.yml\"\u003e\u003cimg src=\"https://github.com/fallow-rs/fallow/actions/workflows/ci.yml/badge.svg\" alt=\"CI\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/fallow-rs/fallow/actions/workflows/coverage.yml\"\u003e\u003cimg src=\"https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/fallow-rs/fallow/badges/coverage.json\" alt=\"Coverage\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://crates.io/crates/fallow-cli\"\u003e\u003cimg src=\"https://img.shields.io/crates/v/fallow-cli.svg\" alt=\"crates.io\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://www.npmjs.com/package/fallow\"\u003e\u003cimg src=\"https://img.shields.io/npm/v/fallow.svg\" alt=\"npm\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/fallow-rs/fallow/blob/main/LICENSE\"\u003e\u003cimg src=\"https://img.shields.io/badge/license-MIT-blue.svg\" alt=\"MIT License\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://docs.fallow.tools\"\u003e\u003cimg src=\"https://img.shields.io/badge/docs-docs.fallow.tools-blue.svg\" alt=\"Documentation\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n---\n\n```bash\nnpx fallow\n```\n\n```\n Dead code   3 unused files, 12 unused exports, 2 unused deps       18ms\n Duplication 4 clone groups (2.1% of codebase)                      31ms\n Complexity  7 functions exceed thresholds                           4ms\n Total       26 issues across 847 files                             53ms\n```\n\n85 framework plugins. No Node.js runtime. No config file needed.\n\n## Install\n\n```bash\nnpx fallow                  # Run without installing\nnpm install -g fallow       # Or install globally (macOS, Linux, Windows)\ncargo install fallow-cli    # Or via Cargo\n```\n\n## Commands\n\n```bash\nfallow                      # Run all three analyses\nfallow dead-code            # Dead code only\nfallow dupes                # Duplication only\nfallow health               # Complexity only\nfallow audit                # Audit changed files (verdict: pass/warn/fail)\nfallow fix --dry-run        # Preview auto-removal of dead exports and deps\nfallow watch                # Re-analyze on file changes\n```\n\n## Dead code\n\nFinds unused files, exports, dependencies, types, enum members, class members, unresolved imports, unlisted dependencies, duplicate exports, circular dependencies (including cross-package cycles in monorepos), boundary violations, type-only dependencies, and test-only production dependencies. Entry points are auto-detected from package.json fields, framework conventions, and plugin patterns.\n\n```bash\nfallow dead-code                          # All dead code issues\nfallow dead-code --unused-exports         # Only unused exports\nfallow dead-code --circular-deps          # Only circular dependencies\nfallow dead-code --boundary-violations    # Only boundary violations\nfallow dead-code --production             # Exclude test/dev files\nfallow dead-code --changed-since main     # Only changed files (for PRs)\nfallow dead-code --group-by owner         # Group by CODEOWNERS for team triage\nfallow dead-code --group-by directory     # Group by first directory component\nfallow dead-code --group-by package       # Group by workspace package (monorepo)\n```\n\n## Duplication\n\nFinds copy-pasted code blocks across your codebase. Suffix-array algorithm -- no quadratic pairwise comparison.\n\n```bash\nfallow dupes                              # Default (mild mode)\nfallow dupes --mode semantic              # Catch clones with renamed variables\nfallow dupes --skip-local                 # Only cross-directory duplicates\nfallow dupes --trace src/utils.ts:42      # Show all clones of code at this location\n```\n\nFour detection modes: **strict** (exact tokens), **mild** (default, AST-based), **weak** (different string literals), **semantic** (renamed variables and literals).\n\n## Complexity\n\nSurfaces the most complex functions in your codebase and identifies where to spend refactoring effort.\n\n```bash\nfallow health                             # Functions exceeding thresholds\nfallow health --score                     # Project health score (0-100) with letter grade\nfallow health --min-score 70              # CI gate: fail if score drops below 70\nfallow health --top 20                    # 20 most complex functions\nfallow health --file-scores               # Per-file maintainability index (0-100)\nfallow health --hotspots                  # Riskiest files (git churn x complexity)\nfallow health --targets                   # Ranked refactoring recommendations\nfallow health --targets --effort low      # Only quick-win refactoring targets\nfallow health --trend                     # Compare against saved snapshot\nfallow health --changed-since main        # Only changed files\n```\n\n## Audit\n\nQuality gate for AI-generated code and PRs. Combines dead code + complexity + duplication scoped to changed files.\n\n```bash\nfallow audit                              # Auto-detects base branch\nfallow audit --base main                  # Explicit base ref\nfallow audit --base HEAD~3               # Audit last 3 commits\nfallow audit --format json                # Structured output with verdict\n```\n\nReturns a verdict: **pass** (exit 0), **warn** (exit 0, warn-severity only), or **fail** (exit 1). JSON output includes a `verdict` field for CI and agent integration.\n\n## CI integration\n\n```yaml\n# GitHub Action\n- uses: fallow-rs/fallow@v2\n\n# GitLab CI — include the template and extend\ninclude:\n  - remote: 'https://raw.githubusercontent.com/fallow-rs/fallow/main/ci/gitlab-ci.yml'\nfallow:\n  extends: .fallow\n\n# Or run directly on any CI\n- run: npx fallow --ci\n```\n\n`--ci` enables SARIF output, quiet mode, and non-zero exit on issues. Also supports:\n\n- `--group-by owner|directory|package` -- group output by CODEOWNERS ownership, directory, or workspace package for team-level triage\n- `--summary` -- show only category counts (no individual issues)\n- `--changed-since main` -- analyze only files touched in a PR\n- `--baseline` / `--save-baseline` -- fail only on **new** issues\n- `--fail-on-regression` / `--tolerance 2%` -- fail only if issues **grew** beyond tolerance\n- `--format sarif` -- upload to GitHub Code Scanning\n- `--format codeclimate` -- GitLab Code Quality inline MR annotations\n- `--format annotations` -- GitHub Actions inline PR annotations (no Action required)\n- `--format json` / `--format markdown` -- for custom workflows (JSON includes machine-actionable `actions` per issue)\n- `--format badge` -- shields.io-compatible SVG health badge (`fallow health --format badge \u003e badge.svg`)\n\nBoth the GitHub Action and GitLab CI template auto-detect your package manager (npm/pnpm/yarn) from lock files, so install/uninstall commands in review comments match your project.\n\nAdopt incrementally -- surface issues without blocking CI, then promote when ready:\n\n```jsonc\n{ \"rules\": { \"unused-files\": \"error\", \"unused-exports\": \"warn\", \"circular-dependencies\": \"off\" } }\n```\n\n### GitLab CI rich MR comments\n\nThe GitLab CI template can post rich comments directly on merge requests -- summary comments with collapsible sections and inline review discussions with suggestion blocks.\n\n| Variable | Default | Description |\n|---|---|---|\n| `FALLOW_COMMENT` | `\"false\"` | Post a summary comment on the MR with collapsible sections per analysis |\n| `FALLOW_REVIEW` | `\"false\"` | Post inline MR discussions at the relevant lines, with `suggestion` blocks for unused exports |\n| `FALLOW_MAX_COMMENTS` | `\"50\"` | Maximum number of inline review comments |\n\nIn MR pipelines, `--changed-since` is set automatically to scope analysis to changed files. Previous fallow comments are cleaned up on re-runs.\n\nThe comment merging pipeline groups unused exports per file and deduplicates clone reports, keeping MR threads readable.\n\nA `GITLAB_TOKEN` (PAT with `api` scope) is recommended for full features (suggestion blocks, cleanup of previous comments). `CI_JOB_TOKEN` works for posting but cannot delete comments from prior runs.\n\n```yaml\n# .gitlab-ci.yml — full example with rich MR comments\ninclude:\n  - remote: 'https://raw.githubusercontent.com/fallow-rs/fallow/main/ci/gitlab-ci.yml'\n\nfallow:\n  extends: .fallow\n  variables:\n    FALLOW_COMMENT: \"true\"       # Summary comment with collapsible sections\n    FALLOW_REVIEW: \"true\"        # Inline discussions with suggestion blocks\n    FALLOW_MAX_COMMENTS: \"30\"    # Cap inline comments (default: 50)\n    FALLOW_FAIL_ON_ISSUES: \"true\"\n```\n\n## Configuration\n\nWorks out of the box. When you need to customize, create `.fallowrc.json` or run `fallow init`:\n\n```jsonc\n// .fallowrc.json\n{\n  \"$schema\": \"https://raw.githubusercontent.com/fallow-rs/fallow/main/schema.json\",\n  \"entry\": [\"src/workers/*.ts\", \"scripts/*.ts\"],\n  \"ignorePatterns\": [\"**/*.generated.ts\"],\n  \"ignoreDependencies\": [\"autoprefixer\"],\n  \"rules\": {\n    \"unused-files\": \"error\",\n    \"unused-exports\": \"warn\",\n    \"unused-types\": \"off\"\n  },\n  \"health\": {\n    \"maxCyclomatic\": 20,\n    \"maxCognitive\": 15\n  }\n}\n```\n\nArchitecture boundary presets enforce import rules between layers with zero manual config:\n\n```jsonc\n{ \"boundaries\": { \"preset\": \"bulletproof\" } } // or: layered, hexagonal, feature-sliced\n```\n\nRun `fallow list --boundaries` to inspect the expanded rules. TOML also supported (`fallow init --toml`). The init command auto-detects your project structure (monorepo layout, frameworks, existing config) and generates a tailored config. It also adds `.fallow/` to your `.gitignore` (cache and local data). Scaffold a pre-commit hook with `fallow init --hooks`. Migrating from knip or jscpd? Run `fallow migrate`.\n\nSee the [full configuration reference](https://docs.fallow.tools/configuration/overview) for all options.\n\n## Framework plugins\n\n85 built-in plugins detect entry points and used exports for your framework automatically.\n\n| Category | Plugins |\n|---|---|\n| **Frameworks** | Next.js, Nuxt, Remix, SvelteKit, Gatsby, Astro, Angular, NestJS, Expo, Electron, and more |\n| **Bundlers** | Vite, Webpack, Rspack, Rsbuild, Rollup, Rolldown, Tsup, Tsdown, Parcel |\n| **Testing** | Vitest, Jest, Playwright, Cypress, Storybook, Mocha, Ava |\n| **Databases** | Prisma, Drizzle, Knex, TypeORM, Kysely |\n| **Monorepos** | Turborepo, Nx, Changesets, Syncpack |\n\n[Full plugin list](https://docs.fallow.tools/frameworks/built-in) -- missing one? Add a [custom plugin](https://docs.fallow.tools/frameworks/custom-plugins) or [open an issue](https://github.com/fallow-rs/fallow/issues).\n\n## Editor \u0026 AI support\n\n- **VS Code extension** -- tree views, status bar, one-click fixes, auto-download LSP binary ([Marketplace](https://github.com/fallow-rs/fallow/tree/main/editors/vscode))\n- **LSP server** -- real-time diagnostics, hover info, code actions, Code Lens with reference counts\n- **MCP server** -- AI agent integration for Claude Code, Cursor, Windsurf ([fallow-skills](https://github.com/fallow-rs/fallow-skills))\n- **JSON `actions` array** -- every issue in `--format json` output includes fix suggestions with `auto_fixable` flag, so agents can self-correct\n\n## Fallow vs linters\n\nLinters enforce style. Formatters enforce consistency. Fallow enforces relevance.\n\nESLint, Biome, and oxlint analyze one file at a time. They catch bad patterns within a file boundary. Fallow builds a module dependency graph across the entire project and finds issues that only appear when you see the whole picture.\n\n| What | Linter | Fallow |\n|---|---|---|\n| Unused variable in a function | yes | no |\n| Unused export that nothing imports | no | yes |\n| File that nothing imports | no | yes |\n| Circular dependency across modules | no | yes |\n| Duplicate code blocks across files | no | yes |\n| Dependency in package.json never imported | no | yes |\n\nThey're complementary -- run your linter on every save, fallow on every commit.\n\n[Full comparison: fallow vs ESLint, Biome, knip, ts-prune](https://docs.fallow.tools/explanations/fallow-vs-linters)\n\n## Performance\n\nBenchmarked on real open-source projects (median of 5 runs, Apple M5).\n\n### Dead code: fallow vs knip\n\n| Project | Files | fallow | knip v5 | knip v6 | vs v5 | vs v6 |\n|:--------|------:|-------:|--------:|--------:|------:|------:|\n| [zod](https://github.com/colinhacks/zod) | 174 | **17ms** | 577ms | 300ms | 34x | 18x |\n| [fastify](https://github.com/fastify/fastify) | 286 | **19ms** | 791ms | 232ms | 41x | 12x |\n| [preact](https://github.com/preactjs/preact) | 244 | **20ms** | 767ms | 2.02s | 39x | 103x |\n| [TanStack/query](https://github.com/TanStack/query) | 901 | **170ms** | 2.50s | 1.28s | 15x | 8x |\n| [svelte](https://github.com/sveltejs/svelte) | 3,337 | **359ms** | 1.73s | 749ms | 5x | 2x |\n| [next.js](https://github.com/vercel/next.js) | 20,416 | **1.66s** | -- | -- | -- | -- |\n\nknip errors out on next.js. fallow completes in under 2 seconds.\n\n### Duplication: fallow vs jscpd\n\n| Project | Files | fallow | jscpd | Speedup |\n|:--------|------:|-------:|------:|--------:|\n| [fastify](https://github.com/fastify/fastify) | 286 | **76ms** | 1.96s | 26x |\n| [vue/core](https://github.com/vuejs/core) | 522 | **124ms** | 3.11s | 25x |\n| [next.js](https://github.com/vercel/next.js) | 20,416 | **2.89s** | 24.37s | 8x |\n\nNo TypeScript compiler, no Node.js runtime. [How it works](https://docs.fallow.tools/explanations/architecture) | [Reproduce benchmarks](https://github.com/fallow-rs/fallow/tree/main/benchmarks)\n\n## Suppressing findings\n\n```ts\n// fallow-ignore-next-line unused-export\nexport const keepThis = 1;\n\n// fallow-ignore-file\n// Suppress all issues in this file\n```\n\nAlso supports `/** @public */` JSDoc tags for library exports consumed externally.\n\n## Limitations\n\nfallow uses syntactic analysis -- no type information. This is what makes it fast, but type-level dead code is out of scope. Use [inline suppression comments](#suppressing-findings) or [`ignoreExports`](https://docs.fallow.tools/configuration/overview#ignoring-specific-exports) for edge cases.\n\n## Documentation\n\n- [Getting started](https://docs.fallow.tools)\n- [Configuration reference](https://docs.fallow.tools/configuration/overview)\n- [CI integration guide](https://docs.fallow.tools/integrations/ci)\n- [Migrating from knip](https://docs.fallow.tools/migration/from-knip)\n- [Plugin authoring guide](https://github.com/fallow-rs/fallow/blob/main/docs/plugin-authoring.md)\n\n## Contributing\n\nMissing a framework plugin? Found a false positive? [Open an issue](https://github.com/fallow-rs/fallow/issues).\n\n```bash\ncargo build --workspace \u0026\u0026 cargo test --workspace\n```\n\n## License\n\nMIT\n","funding_links":["https://github.com/sponsors/bartwaardenburg"],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffallow-rs%2Ffallow","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffallow-rs%2Ffallow","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffallow-rs%2Ffallow/lists"}