{"slug": "fallow-deterministic-codebase-intelligence-for-typescript-and-javascript", "title": "Fallow – Deterministic codebase intelligence for TypeScript and JavaScript", "summary": "Fallow, a deterministic codebase intelligence engine for TypeScript and JavaScript, has been released as a free, open-source tool. It provides static analysis without AI, offering quality reports, risk assessment, and cleanup evidence for humans, CI pipelines, and AI agents. The Rust-native tool requires no configuration and runs sub-second analyses.", "body_md": "**Deterministic codebase intelligence for TypeScript and JavaScript.**\n\nQuality, risk, architecture, dependencies, duplication, and safe cleanup evidence for humans, CI, and agents.\n\nStatic analysis is free and open source. Optional runtime intelligence (Fallow Runtime) adds production execution evidence.\n\n**Rust-native. Zero config. Sub-second. No AI inside the analyzer.**\n\nFallow turns a JS/TS repository into a trusted quality report: health score, changed-code risk, hotspots, duplication, architecture issues, dependency hygiene, and cleanup opportunities. It helps you answer:\n\n- What changed?\n- What got riskier?\n- What should I review?\n- What should I refactor?\n- What can be safely removed?\n\nFallow is built for maintainers, CI pipelines, editors, and AI agents that need structured evidence instead of guesses. No AI inside the analyzer. Fallow produces deterministic findings, typed output contracts, and traceable explanations that downstream tools can trust.\n\nFallow dogfoods its shipped JavaScript and TypeScript surfaces in CI: the VS Code extension and npm wrapper package are analyzed with fallow on every relevant change.\n\nRun a changed-code audit:\n\n```\nnpx fallow audit\n```\n\nExample output:\n\n```\nAudit scope: 7 changed files vs main\n\n-- Dead Code ---------------------------------------\n\nx 7 unused dependencies · 14 dev/optional dependencies\n  21 issues · 1 suppressed · 0 stale suppressions\n\n-- Duplication -------------------------------------\n\nx 3 clone families touching changed files\n\n-- Complexity --------------------------------------\n\n! 2 changed functions above threshold\n```\n\nCleanup opportunities include unused files, unused exports, unused dependencies, stale suppressions, and other code that no longer appears to carry product value.\n\nFor machine-readable output:\n\n```\nnpx fallow audit --format json\n```\n\nFor quality scoring and refactor targets:\n\n```\nnpx fallow health --score --hotspots --targets\n```\n\nFor cleanup-specific findings:\n\n```\nnpx fallow dead-code\n```\n\n122 framework plugins. No Node.js runtime required for static analysis. No config needed for the first run.\n\nFallow is a codebase intelligence engine for TypeScript and JavaScript projects.\n\nIt analyzes your repository as a system, not just as a list of files. It connects static structure, dependency relationships, duplication, complexity, architecture boundaries, package hygiene, and optional runtime evidence into one quality report.\n\nFallow helps teams:\n\n- review risky pull requests before they merge\n- track quality trends over time\n- find architectural hotspots\n- understand dependency hygiene\n- detect duplicated logic\n- explain why code is used, unused, risky, or safe to remove\n- provide structured repo context to AI agents and editor tools\n\nLinters check files. TypeScript checks types. Fallow checks the codebase. Fallow does not use AI to invent findings. It produces deterministic evidence that humans and agents can inspect.\n\n```\nnpm install --save-dev fallow   # or: pnpm add -D fallow / yarn add -D fallow / bun add -d fallow\n```\n\nInstalls the CLI, LSP server, MCP server, and version-matched Agent Skill into `node_modules`\n\n. For one-off CLI use, run `npx fallow`\n\n; Rust users can also run `cargo install fallow-cli`\n\n.\n\nInteractive human runs can show a one-line upgrade hint when a cached latest-version check says the local fallow is stale. Machine formats, CI, quiet runs, and non-TTY agent paths never show the hint; set `FALLOW_UPDATE_CHECK=off`\n\nto disable the hint and background check.\n\nParsing `fallow --format json`\n\nin TypeScript? `import type { CheckOutput } from \"fallow/types\"`\n\ngives you the full output contract, version-pinned to your installed CLI.\n\nBuild the local CLI image from this repository:\n\n```\ndocker build -t fallow:local .\n```\n\nRun fallow against a project by mounting it at `/workspace`\n\n:\n\n```\ncd /path/to/project\ndocker run --rm -v \"$PWD:/workspace\" --user \"$(id -u):$(id -g)\" fallow:local audit --format json --quiet\n```\n\nThe `--user`\n\nmapping keeps `.fallow/`\n\ncaches and generated reports owned by your host user. It also lets `fallow audit`\n\nuse git base detection without Git's dubious-ownership guard blocking the mounted repository. The image includes git, Node.js, npm, and Corepack; fallow does not install your project dependencies automatically.\n\nFor Compose, copy `examples/docker/compose.yaml`\n\ninto the target project after building the image, then run:\n\n```\ndocker compose run --rm fallow audit --format json --quiet\n```\n\nFallow is a one-shot CLI, not a long-running service. In Portainer or other stack tools, use a one-shot run command instead of deploying it as an always-on service, or override the command for an interactive shell. Container exit codes are the fallow process exit codes, so CI can gate on the `docker run`\n\nor `docker compose run`\n\nresult directly.\n\nOn Linux and WSL, the commands above work as written. On Windows outside WSL, pass an absolute project path accepted by Docker Desktop and keep the mounted working directory at `/workspace`\n\n. For containerized runtime coverage inventory, use the container path prefix:\n\n```\ndocker run --rm -v \"$PWD:/workspace\" --user \"$(id -u):$(id -g)\" fallow:local coverage upload-inventory --path-prefix /workspace --format json --quiet\n```\n\nProgrammatic Node API:\n\n```\nnpm install @fallow-cli/fallow-node   # or: pnpm/yarn/bun add @fallow-cli/fallow-node\njs\nimport { detectDeadCode, detectDuplication, computeHealth } from '@fallow-cli/fallow-node';\n\nconst findings = await detectDeadCode({ root: process.cwd() });\nconst dupes = await detectDuplication({ root: process.cwd(), mode: 'mild', minTokens: 30 });\nconst health = await computeHealth({ root: process.cwd(), score: true, ownershipEmails: 'handle' });\n```\n\nA compact health score for the current state of the repository, with targets for maintainability, complexity, duplication, dependency hygiene, and architecture.\n\nChanged-code analysis (`fallow audit`\n\n) that highlights files and symbols most likely to need review before merge. Returns a verdict (pass / warn / fail) and an attribution split between findings the PR introduced and pre-existing ones.\n\nFunctions, files, and packages that combine complexity, churn, size, coupling, and (with the runtime layer) runtime importance.\n\nClone families and duplicated implementation patterns that increase maintenance cost. Four detection modes from exact token match to semantic clones with renamed variables.\n\nCircular dependencies, boundary violations across layers and modules, re-export chains, and other dependency-graph issues. Zero-config presets for bulletproof, layered, hexagonal, and feature-sliced architectures. Framework correctness checks catch Next.js `\"use client\"`\n\nfiles that export a server-only or route-segment config name (such as `metadata`\n\n, `revalidate`\n\n, or a route HTTP method) before the build does. They also flag barrels that re-export both client and server-only modules, and `\"use client\"`\n\n/ `\"use server\"`\n\ndirectives placed below an import where the bundler silently ignores them. Whole-project App Router checks catch route collisions (two files resolving to the same URL across route groups, a `next build`\n\nfailure) and dynamic-segment name conflicts (`[id]`\n\nvs `[slug]`\n\nat the same position), scoped per app-root so monorepos with multiple Next apps are not false-flagged. They also flag Server Actions exported from a `\"use server\"`\n\nfile that no code in the project calls (no import-and-call, no `action={fn}`\n\nbinding, no `<form action={fn}>`\n\n), the cross-file dead-action direction eslint-plugin-next cannot see.\n\nUnused dependencies, unresolved imports, duplicate exports, unlisted imports, type-only production deps, test-only production deps, and pnpm catalog and overrides hygiene.\n\nUnused files, unused exports, unused types, unused enum members, unused class members, unused Pinia store members, unprovided Vue/Svelte injects, stale suppression comments, and code paths that appear safe to review for removal. Opt-in API hygiene checks such as private type leaks live here too.\n\nStatic analysis answers what is connected. Runtime intelligence answers what actually ran in production. Hot paths, cold code, runtime-weighted health, stale flags, runtime-backed PR review. See the [Runtime intelligence](#runtime-intelligence-optional) section below.\n\nStructured JSON, an MCP server, and an LSP for answering \"what depends on this?\", \"why is this used?\", \"what changed?\", and \"what action is safest?\".\n\nFallow gives AI agents structured repo truth instead of forcing them to infer everything from grep.\n\nAgents can ask:\n\n- Who imports this symbol?\n- Why is this export considered used?\n- Why is this export considered unused?\n- What changed in this PR?\n- Which files are risky to touch?\n- Which files are architectural hotspots?\n- What duplicate siblings exist?\n- What cleanup action is safest?\n- What evidence supports this finding?\n\nFallow exposes this through JSON output, typed output contracts (`import type { CheckOutput } from \"fallow/types\"`\n\n), the MCP server, and the LSP. Every issue in `--format json`\n\ncarries a machine-actionable `actions`\n\narray with an `auto_fixable`\n\nflag so agents can self-correct.\n\nRun `fallow init --agents`\n\nto scaffold a starter `AGENTS.md`\n\nfor project-specific navigation, command, architecture, and safety guidance. The file is guidance for agents, not a readiness score, and fallow will not overwrite an existing guide.\n\nCommon agent workflow:\n\n- generate or edit code\n- run\n`fallow audit --format json`\n\n- inspect findings and per-issue\n`actions`\n\n- apply safe fixes or adjust the patch before opening a PR\n- hand the result to a human reviewer with better evidence\n\n```\nnpx fallow audit --format json\nnpx fallow --format json\nnpx fallow --coverage coverage/coverage-final.json --format json\nnpx fallow fix --dry-run --format json\n```\n\nFor full adoption instead of one-off review, see the [Fallow compliance happy path](https://github.com/fallow-rs/fallow/blob/main/docs/fallow-compliance.md). It defines the end state and includes a copy-paste agent onboarding prompt.\n\nSee [Agent integration](https://docs.fallow.tools/integrations/mcp) for MCP setup and the full list of structured tools.\n\nThe MCP server includes `inspect_target`\n\nfor agents that need one evidence bundle for a file or exported symbol, combining trace, dead-code, duplication, complexity, and security signals without inventing a new analysis pass.\n\nThe MCP server also exposes `code_execute`\n\n, a bounded read-only Code Mode tool for composing multiple fallow analysis calls in one JavaScript snippet. It can call analysis helpers such as `fallow.projectInfo`\n\n, `fallow.audit`\n\n, and `fallow.checkHealth`\n\n, but it does not expose mutating fix tools.\n\nFor security review loops, see the [Security agent verification recipe](/fallow-rs/fallow/blob/main/docs/security-agent-verification.md). It shows how to combine `fallow security --format json --surface`\n\n, candidate evidence, and MCP `security_candidates`\n\noutput without adding model calls to fallow core.\n\nRun `fallow impact`\n\nto see what fallow has done for you: how many issues it is surfacing, the trend since your last recorded run, and how many commits its pre-commit gate caught before they shipped. Run `fallow impact --all`\n\nto roll every tracked project into one cross-repo view. It is opt-in (`fallow impact enable`\n\n) and entirely local: history lives in your user config directory (never written into the repo, so nothing to gitignore) and is never uploaded.\n\nProduct telemetry for improving agent, CI, MCP, and editor workflows is off by default. Run `fallow telemetry inspect --example`\n\nto see the payload, or `FALLOW_TELEMETRY=inspect fallow audit --format json --quiet`\n\nto inspect a real run without sending it. Run `fallow telemetry enable`\n\nonly when you want to help improve these integrations. See [Telemetry](/fallow-rs/fallow/blob/main/docs/telemetry.md).\n\nAI accelerates code creation. It does not eliminate review, cleanup, or architecture drift.\n\nWhen Claude Code, Codex, Cursor, or other tools generate changes, teams still need to know:\n\n- did this introduce risky complexity?\n- did it duplicate logic that already existed?\n- did the change cross an architectural boundary it should not cross?\n- did it leave behind unused code or stale dependencies?\n- is this code on a hot path or a cold one?\n- what should the reviewer read closely first?\n\nFallow answers those questions with deterministic, graph-based analysis and structured output, so both humans and agents can act on facts instead of guesses.\n\n```\nfallow                       # Full codebase analysis: cleanup + duplication + health\nfallow audit                 # Audit changed files (verdict: pass/warn/fail)\nfallow health                # Complexity + refactor targets\nfallow dupes                 # Repeated logic\nfallow dead-code             # Cleanup candidates\nfallow security              # Security candidates, hardcoded-secret needs explicit category include\nfallow explain unused-export # Explain a rule without analyzing\nfallow watch                 # Re-analyze on file changes\nfallow fix --dry-run         # Preview automatic cleanup\n```\n\nCombined mode (`fallow`\n\n) and `fallow audit`\n\nsupport per-analysis production mode. Precedence is CLI flags, then environment variables, then config:\n\n```\n{\n  \"production\": {\n    \"health\": true,\n    \"deadCode\": false,\n    \"dupes\": false\n  }\n}\n```\n\nUse `--production-health`\n\n, `--production-dead-code`\n\n, or `--production-dupes`\n\nfor one invocation, or `FALLOW_PRODUCTION_HEALTH=true`\n\nand related env vars in CI. The global `--production`\n\nflag still enables production mode for every analysis.\n\n`fallow security`\n\nremains opt-in and ranks reachable active-code candidates first. It includes source-backed ReDoS regex candidates for risky literal patterns applied to untrusted input, while safe literal patterns and source-free uses stay quiet. When a sink is also reported as dead code, JSON includes `dead_code`\n\ncontext and the command points agents toward deleting the unused file or removing the unused export before hardening that sink. Use `fallow security --gate new --changed-since <ref>`\n\nfor changed-line candidate gating, or `fallow security --gate newly-reachable --changed-since <ref>`\n\nwhen an existing sink becoming entry-reachable should block the change for review. Use the [Security agent verification recipe](/fallow-rs/fallow/blob/main/docs/security-agent-verification.md) to turn raw candidates into verifier-filtered survivors outside fallow core.\n\nPrecedence (highest to lowest): CLI flags, per-analysis env var, global `FALLOW_PRODUCTION`\n\n, config. CLI flags only enable; env vars and config can also disable. Worked examples:\n\n```\n# Run health in production mode, dead-code and dupes on the full tree\nfallow --production-health\n\n# Same, via env var (useful in CI templates that pass env-only)\nFALLOW_PRODUCTION_HEALTH=true fallow\n\n# Per-analysis env wins over the global env, so this runs health in production mode\n# even though the global env says off (the typical CI-template defaults case)\nFALLOW_PRODUCTION=false FALLOW_PRODUCTION_HEALTH=true fallow\n\n# CLI flags beat env vars; this turns ALL three on regardless of any FALLOW_PRODUCTION_* env\nfallow --production\n```\n\nCleanup opportunities are code that no longer appears to carry product value: unused files, exports, dependencies, types, enum members, class members, Pinia store members, unprovided Vue/Svelte injects, unresolved imports, unlisted dependencies, duplicate exports, circular dependencies (including cross-package cycles in monorepos), boundary violations, type-only dependencies, test-only production dependencies, and stale suppression comments. Workspace package dependencies are checked like external packages, so unused or undeclared internal package edges are visible in monorepos. Entry points are auto-detected from package.json fields, package scripts, framework conventions, and plugin patterns. Public class members on classes exposed from non-private package entry points or exportless source subpath indexes are treated as library API surface, while reachable internal classes still get member-level checks. Arrow-wrapped dynamic imports (`React.lazy`\n\n, `loadable`\n\n, `defineAsyncComponent`\n\n) and proven local `child_process.fork()`\n\nrunner targets are tracked as references. Script multiplexers (`concurrently`\n\n, `npm-run-all`\n\n) are analyzed to discover transitive script dependencies. JSDoc tags (`@public`\n\n, `@internal`\n\n, `@beta`\n\n, `@alpha`\n\n, `@expected-unused`\n\n) control export visibility. Private type leaks are currently opt-in API hygiene findings via `--private-type-leaks`\n\nor the `private-type-leaks`\n\nrule.\n\n```\nfallow dead-code                          # All dead code issues\nfallow dead-code --unused-exports         # Only unused exports\nfallow dead-code --private-type-leaks     # Opt-in private type leak API hygiene\nfallow dead-code --circular-deps          # Only circular dependencies\nfallow dead-code --boundary-violations    # Only boundary violations\nfallow dead-code --stale-suppressions     # Only stale suppression comments\nfallow dead-code --production             # Exclude test/dev files\nfallow dead-code --changed-since main     # Only changed files (for PRs)\nfallow dead-code --file src/utils.ts       # Single file (lint-staged integration)\nfallow dead-code --include-entry-exports  # Also check exports from entry files\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)\nfallow dead-code --group-by section       # Group by GitLab CODEOWNERS section\n```\n\nFinds copy-pasted code blocks across your codebase. Suffix-array algorithm -- no quadratic pairwise comparison. Repeated atomic function calls are filtered by default, so long calls to an existing shared abstraction do not show up as refactoring work.\n\n```\nfallow dupes                              # Default (mild mode)\nfallow dupes --mode semantic              # Catch clones with renamed variables\nfallow dupes --skip-local                 # Only cross-directory duplicates\nfallow dupes --group-by owner             # Partition clone groups by CODEOWNERS team\nfallow dupes --group-by directory         # Partition clone groups by directory\nfallow dupes --trace src/utils.ts:42      # Show all clones of code at this location\nfallow dupes --trace dup:7f3a2c1e         # Deep-dive a clone group by its dup:<id> fingerprint\n```\n\nClone fingerprints are usually short `dup:<8hex>`\n\nids and widen only when a\nrare report collision requires it.\n\nFour detection modes: **strict** (exact tokens), **mild** (default, AST-based), **weak** (different string literals), **semantic** (renamed variables and literals).\n\nSurfaces the most complex functions in your codebase and identifies where to spend refactoring effort. Angular templates are included as synthetic `<template>`\n\nentries when they use control flow or complex bindings, both for external `templateUrl`\n\nfiles and inline `@Component({ template: \\`\n\n...` })` decorators.\n\n```\nfallow health                             # Functions/templates 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 --hotspots --ownership      # Add bus factor, owner, drift signals\nfallow health --hotspots --churn-file churn.json   # Import history (non-git VCS: Arc, hg, p4)\nfallow health --workspace @scope/app      # Scope vital signs + score to one package\nfallow health --group-by package --score  # Per-package vital signs + score (monorepos)\nfallow health --targets                   # Ranked refactoring recommendations\nfallow health --targets --effort low      # Only quick-win refactoring targets\nfallow health --coverage-gaps             # Static test coverage gaps\nfallow health --coverage coverage/coverage-final.json\nfallow health --coverage artifacts/coverage.json --coverage-root /home/runner/work/myapp\nfallow health --runtime-coverage ./coverage\nfallow health --runtime-coverage ./coverage --min-invocations-hot 250\nfallow health --trend                     # Compare against saved snapshot\nfallow health --changed-since main        # Only changed files\n```\n\nUse `health.thresholdOverrides`\n\nfor intentional local ceilings on legacy files\nor generated adapters while keeping global thresholds strict. Each override\nmatches `files`\n\nglobs, optionally narrows to exact `functions`\n\n, and can set any\nof `maxCyclomatic`\n\n, `maxCognitive`\n\n, or `maxCrap`\n\n. JSON, compact, markdown, and\nhuman output report active, stale, and full-run no-match override state so\ntemporary exceptions stay visible.\n\nStatic analysis answers: **what is connected to what?**\n\nRuntime intelligence answers: **what actually ran?**\n\nFallow Runtime is the optional paid team layer. It uses runtime coverage as the collection engine (V8 dumps via `NODE_V8_COVERAGE=...`\n\nand Istanbul `coverage-final.json`\n\nfiles), then merges that evidence into `fallow health`\n\nso teams and coding agents can:\n\n- review changes on hot production paths more carefully\n- delete cold code with stronger evidence\n- prioritize refactors by runtime importance\n- spot stale feature-flag branches and stale runtime code\n- give agents factual usage data instead of assumptions\n\n```\nfallow license activate --trial --email you@company.com\nfallow coverage setup\nfallow health --runtime-coverage ./coverage\nfallow coverage analyze --cloud --repo owner/repo --format json\n```\n\nStatic `coverage_gaps`\n\nand runtime `runtime_coverage`\n\nare separate layers in the same `health`\n\nsurface:\n\n| Surface | Flag | Input | Answers | License |\n|---|---|---|---|---|\n| Static test reachability | `--coverage-gaps` |\nnone | which runtime files/exports have no test dependency path | no |\n| Exact CRAP scoring | `--coverage` |\nIstanbul JSON file or `coverage-final.json` directory |\nhow covered each function is for CRAP computation | no |\n| Runtime runtime coverage | `--runtime-coverage` |\nV8 directory, V8 JSON file, or Istanbul JSON file | which functions actually executed, which stayed cold, which are hot | yes |\n\nWhen enough evidence overlaps, `health`\n\nalso emits `coverage_intelligence`\n\n: a combined verdict layer for humans and agents. It turns compound signals into stable findings, for example changed hot paths with high CRAP and low tests, static unused code that was also cold at runtime, cold reachable code with ownership risk, or hot covered code that needs careful refactoring. The block is additive and appears inside `audit`\n\nthrough the nested health result without changing audit's default verdict.\n\nSetup details:\n\n`fallow license activate --trial --email ...`\n\nstarts a trial and stores the signed license locally`fallow license refresh`\n\nrefreshes the stored license before the hard-fail window`fallow coverage setup`\n\ndetects your framework and package manager, installs the sidecar if needed, writes a collection recipe, and resumes from the current setup state on re-run`fallow coverage setup --yes --json`\n\nemits deterministic agent-readable setup instructions without prompts, file writes, installs, or network calls. Add`--explain`\n\nto include a`_meta`\n\nblock with field definitions, enum values, warning semantics, and the docs URL. In workspaces it emits per-runtime-package`members[]`\n\n, unions`runtime_targets`\n\n, prefixes member file paths, and skips pure workspace aggregator roots`fallow coverage analyze --cloud --repo owner/repo --format json`\n\nexplicitly fetches the latest cloud runtime facts for a repo, merges them locally with the current AST/static analysis, and emits the same`runtime_coverage`\n\nJSON block.`FALLOW_API_KEY`\n\nalone does not enable cloud mode; pass`--cloud`\n\n,`--runtime-coverage-cloud`\n\n, or set`FALLOW_RUNTIME_COVERAGE_SOURCE=cloud`\n\n.`fallow coverage upload-inventory`\n\npushes a static function inventory to fallow cloud so the dashboard's`Untracked`\n\nfilter (functions that exist but never run) lights up. Runs in CI, respects`.gitignore`\n\n+`--exclude-paths`\n\n, preserves same-named functions by their line-aware cloud identity, and warns when inventory paths do not overlap recent runtime paths. For containerized deployments, pass`--path-prefix /app`\n\n(or your Dockerfile`WORKDIR`\n\n) so inventory paths match what the runtime beacon reports`fallow coverage upload-source-maps`\n\nuploads build`.map`\n\nfiles from CI so bundled runtime coverage resolves back to original source paths. Defaults to`dist/**/*.map`\n\n,`$GITHUB_SHA`\n\n, and basename matching; pass`--strip-path=false`\n\nwhen coverage reports bundle paths like`assets/app.js`\n\n- Cloud API calls accept\n`FALLOW_CA_BUNDLE=/path/to/bundle.pem`\n\nfor custom PEM trust bundles. The bundle replaces the default WebPKI roots, so private-CA environments should pass a complete trust bundle.`upload-source-maps`\n\nhonors 429`Retry-After`\n\nbackoff, caps waits at 60 seconds, and exits 7 when setup or transport failures prevent every upload. - The sidecar can be installed globally or as a project devDependency; fallow resolves\n`FALLOW_COV_BIN`\n\n, project-local shims, package-manager bin lookups,`~/.fallow/bin/fallow-cov`\n\n, and`PATH`\n\n`fallow health --runtime-coverage <path>`\n\naccepts a V8 directory, a single V8 JSON file, or a single Istanbul coverage map JSON file (commonly`coverage-final.json`\n\n)`fallow health --coverage <path>`\n\naccepts a single Istanbul coverage map JSON file or a directory containing`coverage-final.json`\n\n; bare`fallow --coverage <path>`\n\nforwards the same data to its embedded health analysis`--coverage-root <path>`\n\nmust be an absolute prefix from the Istanbul file paths. Use it when coverage was generated in CI or Docker with a different checkout root, for example`fallow health --coverage artifacts/coverage-final.json --coverage-root /home/runner/work/myapp`\n\n- Standalone health and bare\n`fallow`\n\nresolve each coverage input as CLI flag, then`FALLOW_COVERAGE`\n\n/`FALLOW_COVERAGE_ROOT`\n\n, then`health.coverage`\n\n/`health.coverageRoot`\n\n, then auto-detection where supported - V8 dumps that include Node's\n`source-map-cache`\n\nare remapped through supported source-map paths before analysis, including file paths, relative paths,`webpack://...`\n\n, and`vite://...`\n\n; unsupported virtual schemes safely fall back to raw V8 handling `fallow health --changed-since <ref> --runtime-coverage <path>`\n\npromotes touched hot paths to a`hot-path-touched`\n\nverdict during change review\n\nRuntime coverage is merged into the same human, JSON, SARIF, compact, markdown, and CodeClimate outputs as the rest of the health report.\n\nRead more: [Static vs runtime intelligence](https://docs.fallow.tools/explanations/static-vs-runtime) | [Runtime coverage](https://docs.fallow.tools/analysis/runtime-coverage)\n\nPR risk gate for human and AI-generated code. Combines changed-file cleanup findings from the dead-code pass with changed-file complexity and duplication findings, then emits a verdict.\n\n```\nfallow audit                              # Auto-detects base branch\nfallow audit --base main                  # Explicit base ref\nfallow audit --base HEAD~3               # Audit last 3 commits\nfallow audit --production-health          # Production health, full dead-code/dupes\nfallow audit --coverage artifacts/coverage-final.json --coverage-root /home/runner/work/myapp\nfallow audit --gate all                   # Fail on inherited findings too\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). By default, audit compares the current tree with the base ref and gates only findings introduced by the changeset; inherited findings are counted in JSON `attribution`\n\n, individual issue objects get `introduced: true|false`\n\n, and inherited findings are shown as context. Set `--gate all`\n\nor `audit.gate: \"all\"`\n\nto fail on every finding in changed files without running the extra base-snapshot attribution pass.\n\n`audit`\n\nforwards `--coverage`\n\nand `--coverage-root`\n\nto its health sub-analysis for exact Istanbul-backed CRAP scoring. Relative `--coverage`\n\npaths resolve against `--root`\n\n; `--coverage-root`\n\nmust be an absolute prefix from the coverage data. `FALLOW_COVERAGE`\n\nand `FALLOW_COVERAGE_ROOT`\n\nare used as fallbacks when the corresponding CLI flags are omitted. Health JSON includes `coverage_source`\n\non CRAP findings and `summary.coverage_source_consistency`\n\nwhen those findings use a uniform source or mix Istanbul data with estimates.\n\nAudit caches base snapshots under `.fallow/cache/`\n\nby default and may keep a SHA-scoped temporary git worktree for reuse across runs against the same base ref. Set `cache.dir`\n\nor `FALLOW_CACHE_DIR`\n\nto relocate the persistent analysis cache; relative paths resolve from the project root. When the current checkout has `node_modules`\n\n, audit links it into the base worktree so tsconfig `extends`\n\nchains into installed packages and path aliases resolve like the working tree. Transient worktrees are removed on normal exit. Use `--no-cache`\n\nto disable snapshot and reusable-worktree caching; if a process is force-killed, run `git worktree prune`\n\nto clean up stale `.git/worktrees/fallow-audit-base-*`\n\nentries.\n\n**Per-analysis baselines.** When touching legacy files with pre-existing issues, reuse the baselines saved by the individual subcommands so audit only fails on genuinely new findings:\n\n```\n# Save once from a clean ref\nfallow dead-code --save-baseline fallow-baselines/dead-code.json\nfallow health    --save-baseline fallow-baselines/health.json\nfallow dupes     --save-baseline fallow-baselines/dupes.json\n\n# Feed into audit on every PR\nfallow audit \\\n  --dead-code-baseline fallow-baselines/dead-code.json \\\n  --health-baseline    fallow-baselines/health.json \\\n  --dupes-baseline     fallow-baselines/dupes.json\n```\n\nKeep committed baselines outside `.fallow/`\n\n; that directory is for cache and local data and is typically gitignored. `fallow-baselines/`\n\nis the recommended default. Configure defaults in `.fallowrc.json`\n\nunder `audit.deadCodeBaseline`\n\n/ `audit.healthBaseline`\n\n/ `audit.dupesBaseline`\n\nso CI stays one command (`fallow audit`\n\n). CLI flags override config.\n\nUse the GitHub Action when you want fallow to handle installation, caching, PR scoping, annotations, review comments, SARIF, and job-summary formatting.\n\n```\nname: Fallow\n\non:\n  pull_request:\n\npermissions:\n  contents: read\n  pull-requests: write # needed for comment/review-comments\n\njobs:\n  fallow:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          fetch-depth: 0 # best diff precision for --changed-since and hotspots\n      - uses: fallow-rs/fallow@v2\n        with:\n          command: audit\n          comment: true\n          review-comments: true\n```\n\n`command: audit`\n\nis the PR gate. In pull requests, the Action auto-scopes to the PR base SHA when `changed-since`\n\nis not set, derives a unified diff for line-level filtering, and emits a verdict: **pass**, **warn**, or **fail**. With the default `fail-on-issues: true`\n\n, audit fails the job only on verdict `fail`\n\n; warn-tier findings stay visible without blocking merge.\n\nUseful GitHub Action modes:\n\n```\n# Rich PR feedback without GitHub Advanced Security\n- uses: fallow-rs/fallow@v2\n  with:\n    command: audit\n    annotations: true        # default: inline workflow annotations\n    comment: true            # sticky PR summary\n    review-comments: true    # inline review comments with suggestions\n    review-guidance: false   # set true for collapsed \"What to do\" blocks\n    diff-filter: added       # added | diff_context | file | nofilter\n    max-comments: 50\n\n# GitHub Code Scanning upload\npermissions:\n  contents: read\n  security-events: write\nsteps:\n  - uses: actions/checkout@v4\n    with:\n      fetch-depth: 0\n  - uses: fallow-rs/fallow@v2\n    with:\n      command: audit\n      sarif: true\n\n# Security delta gate\n- uses: fallow-rs/fallow@v2\n  with:\n    command: security\n    changed-since: ${{ github.event.pull_request.base.sha }}\n    security-gate: new           # or newly-reachable\n\n# Health score, trend, hotspots, and refactor targets\n- uses: fallow-rs/fallow@v2\n  with:\n    command: health\n    score: true\n    trend: true\n    save-snapshot: true\n    hotspots: true\n    targets: true\n\n# Monorepo scoping\n- uses: fallow-rs/fallow@v2\n  with:\n    command: audit\n    changed-workspaces: origin/main\n\n# Keep generated action artifacts out of the workspace root\n- uses: fallow-rs/fallow@v2\n  with:\n    command: audit\n    artifacts-dir: .var/fallow\n\n# Coverage-backed CRAP scoring in audit or the default combined run\n- uses: fallow-rs/fallow@v2\n  with:\n    command: audit\n    max-crap: 30\n    coverage: artifacts/coverage-final.json\n    coverage-root: /home/runner/work/myapp\n\n# Runtime evidence, licensed Fallow Runtime layer\n- uses: fallow-rs/fallow@v2\n  with:\n    command: health\n    score: true\n    runtime-coverage: artifacts/v8-coverage\n    min-invocations-hot: 100\n```\n\nAction outputs include:\n\n`issues`\n\n-- command-specific issue count; for audit, this is gate-aware`verdict`\n\n-- audit verdict (`pass`\n\n,`warn`\n\n,`fail`\n\n)`gate`\n\n-- audit gate (`new-only`\n\nor`all`\n\n) or security gate (`new`\n\nor`newly-reachable`\n\n)`results`\n\n/`sarif`\n\n-- generated artifact paths`changed-files-unavailable`\n\n--`true`\n\nif PR file enumeration degraded and analysis ran less scoped than expected`dedup-lookup-failed`\n\n/`post-skipped-reason`\n\n-- comment/review posting degradation signals\n\nSet `artifacts-dir`\n\nto write generated files such as `fallow-results.json`\n\n, `fallow-results.sarif`\n\n, `fallow-stderr.log`\n\n, and `fallow-analysis-args.sh`\n\nunder a project-local generated directory. The default is `.`\n\nfor backward compatibility, and the `results`\n\n/ `sarif`\n\noutputs report the resolved paths for downstream steps.\n\nSARIF upload requires GitHub Code Scanning, which is available on public repositories and on private repositories with GitHub Advanced Security enabled. If it is unavailable, the Action skips upload with a warning and leaves the job summary, annotations, comments, and JSON output intact.\n\nGitHub inline review comments target the current PR file state (`side: RIGHT`\n\n). Findings on deleted lines are not modeled yet; fallow's diagnostics are current-state oriented in normal use.\n\nFor GitLab, use the bundled template. It installs fallow, sets `GIT_DEPTH: \"0\"`\n\n, caches `.fallow/`\n\n, produces Code Quality reports by default, and can post summary comments and inline MR discussions.\n\n```\n# GitLab CI -- remote include\ninclude:\n  - remote: 'https://raw.githubusercontent.com/fallow-rs/fallow/vX.Y.Z/ci/gitlab-ci.yml'\n\nfallow:\n  extends: .fallow\n  variables:\n    FALLOW_COMMAND: \"audit\"\n    FALLOW_COMMENT: \"true\"\n    FALLOW_SUMMARY_SCOPE: \"diff\"\n    FALLOW_REVIEW: \"true\"\n    FALLOW_REVIEW_GUIDANCE: \"true\"\n    FALLOW_MAX_CRAP: \"30\"\n    FALLOW_COVERAGE: \"artifacts/coverage-final.json\"\n    FALLOW_COVERAGE_ROOT: \"/home/runner/work/myapp\"\n```\n\nWhen `FALLOW_COMMAND`\n\nis empty, the template runs bare `fallow`\n\nand forwards `FALLOW_COVERAGE`\n\nplus `FALLOW_COVERAGE_ROOT`\n\nto the embedded health pass. The GitHub Action does the same for an empty `command`\n\ninput.\n\nTo gate only security candidates introduced by a merge request, use `FALLOW_COMMAND: \"security\"`\n\nand `FALLOW_SECURITY_GATE: \"new\"`\n\n. Use `newly-reachable`\n\nwhen an existing candidate becoming reachable from entry points should block review. Security gate failures exit 8, and the wrapper count reflects only the candidates that matched the selected gate.\n\n`FALLOW_COMMENT`\n\nand `FALLOW_REVIEW`\n\nrequire `GITLAB_TOKEN`\n\nwith API scope. In MR pipelines, the template auto-sets `FALLOW_CHANGED_SINCE`\n\nfrom the MR diff base SHA when possible and derives `FALLOW_DIFF_FILE`\n\nfor line-level filtering. For monorepos, set `FALLOW_CHANGED_WORKSPACES: \"origin/main\"`\n\nto scope analysis to touched workspaces. Set `FALLOW_SUMMARY_SCOPE=diff`\n\nwhen the sticky summary should hide pre-existing project-level findings outside the diff.\n\n`FALLOW_REVIEW`\n\nuses the typed `review-gitlab`\n\nenvelope v2, not scraped human output. That gives the template stable v2 fingerprints, same-line comment merging, UTF-8-safe body truncation, stale-thread reconciliation via `fallow ci reconcile-review`\n\n, and GitLab diff positions for inline discussions. The review script fetches MR `diff_refs`\n\nautomatically; set `FALLOW_GITLAB_BASE_SHA`\n\n, `FALLOW_GITLAB_START_SHA`\n\n, or `FALLOW_GITLAB_HEAD_SHA`\n\nonly when your runner needs explicit overrides.\n\n```\n# GitLab CI -- vendored include when runners cannot reach GitHub raw\n# Run once locally: npx fallow ci-template gitlab --vendor\n# Commit the generated ci/ + action/ files.\ninclude:\n  - local: 'ci/gitlab-ci.yml'\n\nfallow:\n  extends: .fallow\n```\n\nFor any other CI system, call the CLI directly:\n\n```\n# PR gate with changed-file attribution\nnpx fallow audit --changed-since origin/main --format json --quiet\n\n# SARIF for code scanning systems\nnpx fallow --ci\n\n# Line-level PR filtering from a unified diff\ngit diff --unified=0 origin/main...HEAD > fallow-pr.diff\nnpx fallow audit --changed-since origin/main --diff-file fallow-pr.diff\n\n# Health score gate\nnpx fallow health --score --min-score 80 --quiet\n\n# Security candidate gate\nnpx fallow security --gate new --changed-since origin/main --format json --quiet\n```\n\nCommon CI flags:\n\n`--group-by owner|directory|package|section`\n\n-- group output by CODEOWNERS ownership, directory, workspace package, or GitLab CODEOWNERS`[Section]`\n\nheaders for team-level triage`--summary`\n\n-- show only category counts (no individual issues)`--changed-since main`\n\n-- analyze only files touched in a PR`--diff-file <path>`\n\n/`--diff-stdin`\n\n-- filter source-anchored findings to added diff hunks, while project-level package findings bypass analysis line filtering. Sticky summary comments can use`FALLOW_SUMMARY_SCOPE=diff`\n\nto filter project-level findings too`--changed-workspaces origin/main`\n\n-- scope monorepo analysis to workspaces containing any changed file (CI primitive; fails hard on git errors so CI never silently widens back to the full repo)`fallow security --gate new|newly-reachable`\n\n-- fail only on security candidates introduced by the change or newly reachable from entry points. The official Action exposes this as`security-gate`\n\n, and GitLab exposes it as`FALLOW_SECURITY_GATE`\n\n`--baseline`\n\n/`--save-baseline`\n\n-- fail only on**new** issues for individual analyses; audit uses the per-analysis baselines shown above`--fail-on-regression`\n\n/`--tolerance 2%`\n\n-- fail only if issues**grew** beyond tolerance`--format sarif`\n\n-- upload to GitHub Code Scanning`--format codeclimate`\n\n-- GitLab Code Quality inline MR annotations`--format pr-comment-github`\n\n/`--format pr-comment-gitlab`\n\n-- typed sticky PR/MR comment markdown`--format review-github`\n\n/`--format review-gitlab`\n\n-- typed inline review envelopes for CI scripts`--format annotations`\n\n-- GitHub Actions inline PR annotations (no Action required)`--format json`\n\n/`--format markdown`\n\n-- for custom workflows (JSON includes machine-actionable`actions`\n\nper issue)`--format badge`\n\n-- shields.io-compatible SVG health badge (`fallow health --format badge > badge.svg`\n\n)\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```\n{ \"rules\": { \"unused-files\": \"error\", \"unused-exports\": \"warn\", \"circular-dependencies\": \"off\" } }\n```\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` |\n`\"false\"` |\nPost a summary comment on the MR with collapsible sections per analysis |\n`FALLOW_REVIEW` |\n`\"false\"` |\nPost inline MR discussions from the typed `review-gitlab` envelope v2, with stable fingerprints, suggestions, dedupe, and stale-thread reconciliation |\n`FALLOW_REVIEW_GUIDANCE` |\n`\"false\"` |\nAdd collapsed \"What to do\" guidance blocks to inline review discussions |\n`FALLOW_MAX_COMMENTS` |\n`\"50\"` |\nMaximum number of inline review comments |\n`FALLOW_SUMMARY_SCOPE` |\n`\"all\"` |\nSticky MR summary scope. Use `all` to include project-level dependency/catalog/override findings even when their anchor line is outside the diff; use `diff` to apply the diff filter to those findings too. Inline review comments are unaffected |\n`FALLOW_DIFF_FILTER` |\n`\"added\"` |\nFilter line-level findings to added diff hunks by default; use `diff_context` , `file` , or `nofilter` to widen review scope |\n`FALLOW_GITLAB_BASE_SHA` / `FALLOW_GITLAB_START_SHA` / `FALLOW_GITLAB_HEAD_SHA` |\n`\"\"` |\nOptional overrides for the GitLab MR `diff_refs` used to build inline discussion positions |\n`FALLOW_SCRIPTS_REF` |\n`\"\"` |\nPinned tag or commit for remote MR-integration scripts; leave empty to prefer vendored local `ci/` + `action/` scripts |\n`FALLOW_VERSION` |\n`\"\"` |\nFallow version to install. Empty reads the project's `package.json` `fallow` dependency, then falls back to `latest` ; set explicitly to override the local pin |\n\nIn MR pipelines, `--changed-since`\n\nis set automatically to scope analysis to changed files, and the comment / review scripts derive a unified diff so inline discussions stay on touched lines by default. Fallow edits sticky comments in place and fingerprints inline review comments so repeated runs can skip duplicates. `FALLOW_SUMMARY_SCOPE=diff`\n\nkeeps the sticky summary focused too: a pre-existing unused dependency in an unrelated package is hidden, while a newly added unused dependency in a changed `package.json`\n\nremains visible. If the diff cannot be fetched or read, fallow keeps the existing fail-open behavior and reports all findings.\n\nThe v2 review envelope keeps MR threads readable by grouping findings that land on the same path and line into one comment, preserving a machine-readable `marker_regex`\n\n, and carrying GitLab `position`\n\ndata (`old_path`\n\n, `new_path`\n\n, `new_line`\n\n, and diff refs) for reliable inline discussions, including renamed files.\n\nFor remote includes, pin the template to a release tag and keep `FALLOW_SCRIPTS_REF`\n\non the same tag or commit. If your GitLab runners cannot reach `raw.githubusercontent.com`\n\n, run `npx fallow ci-template gitlab --vendor`\n\nlocally, commit the generated `ci/`\n\nand `action/`\n\nfiles, and use GitLab's local include syntax. The vendored template prefers local scripts and skips the remote fetch path entirely.\n\nA `GITLAB_TOKEN`\n\n(PAT or project access token with `api`\n\nscope) is required for summary comments and inline MR discussions. GitLab's documented `CI_JOB_TOKEN`\n\npermissions allow reading MR notes, but not creating, updating, or deleting them. `CI_JOB_TOKEN`\n\nis still useful for GitLab package registry authentication.\n\nGitLab setup gotchas:\n\n- The template sets\n`GIT_STRATEGY: \"fetch\"`\n\nso shared templates that set`GIT_STRATEGY=none`\n\ndo not leave fallow without a working tree. - The template sets\n`GIT_DEPTH: \"0\"`\n\nso`--changed-since`\n\ncan diff against the MR base SHA without shallow-clone ambiguity. - For private GitLab npm registries, create\n`.npmrc`\n\nduring the job with`${CI_PROJECT_ID}`\n\nand`${CI_JOB_TOKEN}`\n\nrather than committing tokens. - For pnpm projects with\n`minimumReleaseAge`\n\n, add`fallow`\n\nand`@fallow-cli/*`\n\nto`minimumReleaseAgeExclude`\n\nwhen you need to consume a just-published fallow release immediately.\n\n```\n# .gitlab-ci.yml -- full example with rich MR comments\ninclude:\n  - remote: 'https://raw.githubusercontent.com/fallow-rs/fallow/vX.Y.Z/ci/gitlab-ci.yml'\n\nfallow:\n  extends: .fallow\n  variables:\n    FALLOW_COMMENT: \"true\"       # Summary comment with collapsible sections\n    FALLOW_SUMMARY_SCOPE: \"diff\" # Filter project-level findings in the sticky summary too\n    FALLOW_REVIEW: \"true\"        # Inline discussions with suggestion blocks\n    FALLOW_REVIEW_GUIDANCE: \"true\" # Collapsed \"What to do\" blocks in inline discussions\n    FALLOW_MAX_COMMENTS: \"30\"    # Cap inline comments (default: 50)\n    FALLOW_SCRIPTS_REF: \"vX.Y.Z\" # Match the pinned template ref when using remote scripts\n    FALLOW_FAIL_ON_ISSUES: \"true\"\n```\n\nWorks out of the box. When you need to customize, create `.fallowrc.json`\n\nor run `fallow init`\n\n:\n\n```\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  \"ignoreUnresolvedImports\": [\"@example/icons\", \"@example/icons/**\"],\n  \"ignoreExportsUsedInFile\": true,\n  \"rules\": {\n    \"unused-files\": \"error\",\n    \"unused-exports\": \"warn\",\n    \"unused-types\": \"off\"\n  },\n  \"health\": {\n    \"maxCyclomatic\": 20,\n    \"maxCognitive\": 15,\n    \"maxCrap\": 30,\n    \"crapRefactorBand\": 5,\n    \"thresholdOverrides\": [\n      {\n        \"files\": [\"src/legacy/**\"],\n        \"functions\": [\"legacyFlow\"],\n        \"maxCyclomatic\": 30,\n        \"maxCognitive\": 25,\n        \"reason\": \"legacy migration\"\n      }\n    ]\n  },\n  \"cache\": {\n    \"dir\": \".cache/fallow\"\n  },\n  \"fix\": {\n    \"catalog\": {\n      \"deletePrecedingComments\": \"auto\"\n    }\n  }\n}\n```\n\nFallow recognizes four config file names. Precedence is first-match-wins per directory, walking up to the workspace root:\n\n`.fallowrc.json`\n\n> `.fallowrc.jsonc`\n\n> `fallow.toml`\n\n> `.fallow.toml`\n\n`.fallowrc.json`\n\naccepts JSONC: comments and trailing commas are allowed.\n`.fallowrc.jsonc`\n\nis identical in behavior; the `.jsonc`\n\nextension exists only\nas a hint to editors that comments are expected. Pick whichever your tooling\nprefers. If more than one of these files coexists in the same directory, fallow\nloads the higher-precedence one and prints a warning on stderr naming the file\nit ignored, so a stale config left over from a migration cannot silently win.\n\n`fix.catalog.deletePrecedingComments`\n\ncontrols how `fallow fix`\n\nhandles YAML\ncomment blocks immediately above removed pnpm catalog entries: `\"auto\"`\n\ndeletes\nblocks that clearly belong to the entry, `\"always\"`\n\ndeletes every contiguous\nleading block, and `\"never\"`\n\npreserves them. To protect a specific comment\nregardless of policy, mark any line in the block with `# fallow-keep`\n\n:\n\n```\ncatalog:\n  # fallow-keep: audit trail, CVE-2024-XXXX\n  react: ^18.2.0\n```\n\nSection-banner comments (3+ repeated `=`\n\n, `-`\n\n, `*`\n\n, `_`\n\n, `~`\n\n, `+`\n\n, or `#`\n\ncharacters, e.g. `# === React 18 production pins ===`\n\n) are also preserved by\nthe `\"auto\"`\n\npolicy so curated dividers survive cleanup.\n\nArchitecture boundary presets enforce import rules between layers with zero manual config:\n\n```\n{ \"boundaries\": { \"preset\": \"bulletproof\" } } // or: layered, hexagonal, feature-sliced\n```\n\nFor custom feature-module boundaries, `autoDiscover`\n\nturns each immediate child\ndirectory into its own zone while rules still reference the logical parent:\n\n```\n{\n  \"boundaries\": {\n    \"zones\": [\n      { \"name\": \"app\", \"patterns\": [\"src/app/**\"] },\n      { \"name\": \"features\", \"patterns\": [\"src/features/**\"], \"autoDiscover\": [\"src/features\"] },\n      { \"name\": \"shared\", \"patterns\": [\"src/shared/**\"] }\n    ],\n    \"rules\": [\n      { \"from\": \"app\", \"allow\": [\"features\", \"shared\"] },\n      { \"from\": \"features\", \"allow\": [\"shared\"] }\n    ]\n  }\n}\n```\n\nWhen an `autoDiscover`\n\nzone also has `patterns`\n\n, discovered child zones are matched first and top-level files fall back to the parent zone. The parent rule automatically allows its discovered children, so `src/features/index.ts`\n\nbarrels can re-export feature modules while non-barrel top-level files such as `src/features/types.ts`\n\nstill follow the parent `features`\n\nrule. Omit `patterns`\n\nwhen you want only discovered child directories classified.\n\nTo catch files that are reachable but assigned to no zone, enable boundary coverage:\n\n```\n{\n  \"boundaries\": {\n    \"zones\": [{ \"name\": \"domain\", \"patterns\": [\"src/domain/**\"] }],\n    \"coverage\": {\n      \"requireAllFiles\": true,\n      \"allowUnmatched\": [\"src/generated/**\"]\n    }\n  }\n}\n```\n\n`requireAllFiles`\n\nreports unzoned source files as `boundary_coverage_violations`\n\n. Use `allowUnmatched`\n\nfor generated files or other intentionally unzoned paths.\n\nTo ban specific calls from a zone (for example, keeping a domain layer free of process execution and logging), add a forbidden-call policy:\n\n```\n{\n  \"boundaries\": {\n    \"zones\": [{ \"name\": \"domain\", \"patterns\": [\"src/domain/**\"] }],\n    \"calls\": {\n      \"forbidden\": [\n        { \"from\": \"domain\", \"callee\": \"child_process.*\" },\n        { \"from\": \"domain\", \"callee\": [\"console.*\", \"process.exit\"] }\n      ]\n    }\n  }\n}\n```\n\nEach matching call reports as a `boundary_call_violations`\n\nfinding naming the written callee, the matched pattern, and the zone. Matching is segment-aware, not substring or glob: `fetch`\n\nmatches only `fetch`\n\n(never `myfetch`\n\n), a trailing `object.*`\n\nmatches any member (`child_process.*`\n\nmatches `child_process.exec`\n\n), and a leading `*.member`\n\nmatches any object (`*.innerHTML`\n\nmatches `el.innerHTML`\n\n). Patterns also match through import provenance, so `child_process.*`\n\ncovers `import { execSync } from \"node:child_process\"`\n\n, the bare `child_process`\n\nspecifier, and namespace imports alike. Two scope rules to know: forbidden-call rules apply only to files classified into a zone (unzoned files are unrestricted; combine with `coverage.requireAllFiles`\n\nto force zoning first), and the check matches direct callee paths only (optional chaining included), so it does not follow aliased or re-bound callees (`const run = cp.exec; run()`\n\n), computed members, or injected dependencies (`this.client.exec()`\n\nwill not match `child_process.*`\n\n). Findings share the `boundary-violation`\n\nrule severity and suppression token; the rule-id-shaped `boundary-call-violation`\n\ntoken is accepted as an alias, and either token suppresses the whole boundary family.\n\nForbidden-call findings inherit the `boundary-violation`\n\nrule, which defaults to `error`\n\nand fails CI on the first matching call. For a staged rollout, start with `\"rules\": { \"boundary-violation\": \"warn\" }`\n\nwhile you triage existing violations, then switch back to `error`\n\nto enforce.\n\nTo encode project policy that is not tied to architecture zones, list declarative rule packs. A rule pack is a standalone JSON or JSONC file of `banned-call`\n\nand `banned-import`\n\nrules, loaded as pure data (no project code ever executes):\n\n```\n// .fallowrc.json\n{ \"rulePacks\": [\"./rule-packs/team-policy.jsonc\"] }\n// rule-packs/team-policy.jsonc\n{\n  \"version\": 1,\n  \"name\": \"team-policy\",\n  \"rules\": [\n    {\n      \"id\": \"no-child-process\",\n      \"kind\": \"banned-call\",\n      \"callees\": [\"child_process.*\"],\n      \"message\": \"Use the sandboxed runner instead.\",\n      \"severity\": \"error\"\n    },\n    {\n      \"id\": \"no-moment\",\n      \"kind\": \"banned-import\",\n      \"specifiers\": [\"moment\"],\n      \"message\": \"Use date-fns.\"\n    }\n  ]\n}\n```\n\nMatches report as `policy-violation`\n\nfindings identified by `<pack>/<rule-id>`\n\nacross every output format. `banned-call`\n\nmatching is segment-aware and import-resolved like `boundaries.calls.forbidden`\n\n, so `child_process.*`\n\ncovers named, namespace, and default imports from `child_process`\n\nand `node:child_process`\n\n; bare patterns like `fetch`\n\nmatch globals by their written path. `banned-import`\n\nmatches the RAW specifier segment-aware: `moment`\n\ncovers `moment`\n\nand `moment/locale/nl`\n\nbut never `moment-timezone`\n\n, and aliased or rewritten specifiers (for example Deno-style `npm:moment`\n\n) are not matched, so list the form your code actually writes. Rules scope with optional `files`\n\n/ `exclude`\n\nglobs, skip type-only imports with `\"ignoreTypeOnly\": true`\n\n, and carry an optional per-rule `severity`\n\n. The `rules.\"policy-violation\"`\n\nmaster defaults to `warn`\n\nso a new pack never hard-fails CI on its first run; opt individual rules up with `\"severity\": \"error\"`\n\nonce triaged (`off`\n\non the master is a kill switch for the whole evaluator). The exit-code gate reads the effective per-finding severity, so one error-severity rule fails the run even under a warn master. Suppress one rule with `// fallow-ignore-next-line policy-violation:<pack>/<rule-id>`\n\nor `// fallow-ignore-file policy-violation:<pack>/<rule-id>`\n\n. Use bare `policy-violation`\n\nonly when you intend to suppress every rule-pack finding at that line or file scope. Pack names and rule ids must use ASCII letters, digits, `.`\n\n, `_`\n\n, or `-`\n\nso scoped tokens are unambiguous. When several rules in scope could match the same usage, `banned-call`\n\nreports one finding per unique callee path (the first applicable rule in config order wins, mirroring `boundaries.calls.forbidden`\n\n), while `banned-import`\n\nreports one finding per matching rule, since each rule carries its own message and severity. Keep pack files in a committed directory such as `rule-packs/`\n\n; `.fallow/`\n\nis the gitignored cache directory, so packs stored there silently vanish from teammates' checkouts. Run `fallow rule-pack-schema`\n\nto print the pack JSON Schema for editor autocomplete; invalid packs (unknown rule kind, missing file, inert pattern, ambiguous names, duplicate ids) fail config load instead of silently enforcing nothing. Rule packs are and will stay pure data: if a future version ever adds executable checks, they will sit behind an explicit trust opt-in, never default-on.\n\nRun `fallow list --boundaries`\n\nto inspect the expanded rules. TOML also supported (`fallow init --toml`\n\n). The init command auto-detects your project structure (monorepo layout, frameworks, existing config) and generates a tailored config. It also adds `.fallow/`\n\nto your `.gitignore`\n\n(cache and local data). Use `fallow init --agents`\n\nto scaffold a starter `AGENTS.md`\n\nwith project-specific guidance for coding agents. Scaffold a pre-commit `fallow audit`\n\nhook with `fallow hooks install --target git`\n\n; the hook uses the current branch upstream as its base and falls back to `--branch`\n\n(or the detected default branch) when no upstream is set. For agent gates, use `fallow hooks install --target agent`\n\n. Migrating from knip or jscpd? Run `fallow migrate`\n\n.\n\nUse `ignoreUnresolvedImports`\n\nfor generated or runtime-provided import specifiers that fallow cannot resolve. Patterns match the raw import string, not a filesystem path: list both `@example/icons`\n\nand `@example/icons/**`\n\nwhen you need the bare package and its subpaths. Parent-relative generated specifiers such as `../generated/**`\n\nare allowed. Keep patterns narrow, since broad values like `**`\n\ncan hide real missing modules. This setting affects only `unresolved-import`\n\nfindings; it does not change dependency usage or resolver behavior.\n\nSee the [full configuration reference](https://docs.fallow.tools/configuration/overview) for all options.\n\n122 built-in plugins detect entry points, convention exports, config-defined aliases, and template-visible usage for your framework automatically.\n\n| Category | Plugins |\n|---|---|\nFrameworks |\nNext.js, Nuxt, Pinia, Remix, Qwik, SvelteKit, Gatsby, Astro, Angular, NestJS, AdonisJS, Contentlayer, Fumadocs, Lit, Obsidian, Ember, Expo, Expo Router, Electron, and more |\nBundlers |\nVite, Webpack, Rspack, Rsbuild, Rollup, Rolldown, Tsup, Tsdown, pkg-utils, Parcel |\nTesting |\nVitest, Jest, Playwright, Cypress, Storybook, Stryker, Mocha, Ava, tap, tsd |\nCI/CD & Release |\nDanger, Commitlint, Commitizen, Semantic Release |\nDeployment |\nVercel, Wrangler, Sentry, OpenNext Cloudflare |\nCSS |\nTailwind, PostCSS, UnoCSS, PandaCSS |\nDatabases & Backend |\nPrisma, Drizzle, Knex, TypeORM, Kysely, Convex |\nBlockchain |\nHardhat |\nMonorepos |\nTurborepo, Nx, Changesets, Syncpack, pnpm |\ni18n |\nWuchale, next-intl, i18next |\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\nFallow is not an AI assistant. It is the deterministic codebase intelligence layer that your assistant, your editor, and your CI pipeline can call.\n\n**Editor integrations**-- VS Code extension, Zed extension, and Neovim LSP setup ([editors](https://github.com/fallow-rs/fallow/tree/main/editors))**LSP server**-- real-time diagnostics, hover info, code actions, Code Lens with reference counts** Agent Skill + MCP server**-- version-matched AI agent guidance ships in the npm package, with MCP integration for Claude Code, Codex, Cursor, Windsurf, and other agents ([fallow-skills](https://github.com/fallow-rs/fallow-skills))**MCP Code Mode**--`code_execute`\n\ncomposes read-only analysis calls in a bounded JavaScript sandbox, without filesystem, network, shell, or fix-apply access**JSON**-- every issue in`actions`\n\narray`--format json`\n\noutput includes fix suggestions with`auto_fixable`\n\nflag, so agents can self-correct**Typed output contract**--`import type { CheckOutput } from \"fallow/types\"`\n\nversion-pinned to your installed CLI**Opt-in telemetry controls**--`fallow telemetry status|inspect|enable|disable`\n\n, with agent-source attribution through`FALLOW_AGENT_SOURCE`\n\nBenchmarked on real open-source projects, cold runs (no cache) so both tools work from scratch, median of 5 runs with 2 warmups. fallow 2.91.0, knip 5.87.0 and 6.6.1, Apple M5, Node 22. Fastest tool per row in bold.\n\n| Project | Files | fallow | knip v5 | knip v6 |\n|---|---|---|---|---|\n|\n\n**35ms**[preact](https://github.com/preactjs/preact)** 49ms**[fastify](https://github.com/fastify/fastify)** 50ms**[vue/core](https://github.com/vuejs/core)** 142ms**[TanStack/query](https://github.com/TanStack/query)** 447ms**[vite](https://github.com/vitejs/vite)** 897ms**[astro](https://github.com/withastro/astro)** 1.29s**[svelte](https://github.com/sveltejs/svelte)** 779ms**[TypeScript](https://github.com/microsoft/TypeScript)** 804ms**[next.js](https://github.com/vercel/next.js)** 3.35s**fallow is fastest on small-to-medium projects (roughly 5-18x faster than knip v5 and 2.7-9x than knip v6; preact is an outlier where knip v6 happens to be slow). On large projects, knip v6's Oxc-based parser is competitive or faster (astro, TypeScript), there, fallow's edge is doing more in one tool, not raw dead-code speed. fallow's persistent cache makes repeat (warm) runs faster again; the table uses the conservative cold numbers.\n\n¹ knip loads and executes a project's config and JSON files to read plugin settings, which is its design and works well on apps. A few framework monorepos trip that up where fallow (purely syntactic, no config execution) completes with no setup: **vite**, a workspace `package.json`\n\ncarries a UTF-8 BOM that knip's `JSON.parse`\n\nrejects (a robustness gap, reportable upstream); **vue/core**, a private `sfc-playground/vite.config.ts`\n\nfails to load; **next.js**, the framework's own monorepo needs a build for its jest config and per-workspace entry config for its `dist`\n\n-published packages (this is the framework source, not a Next.js app, which is what knip's Next.js plugin targets). All are fixable with knip config; the point is fallow needs none.\n\n| Project | Files | fallow | jscpd | Speedup |\n|---|---|---|---|---|\n|\n\n**90ms**[fastify](https://github.com/fastify/fastify)** 100ms**[vue/core](https://github.com/vuejs/core)** 204ms**[TanStack/query](https://github.com/TanStack/query)** 173ms**[svelte](https://github.com/sveltejs/svelte)** 366ms**[next.js](https://github.com/vercel/next.js)** 3.87s**No TypeScript compiler, no Node.js runtime needed to analyze your code. [Fallow vs linters](https://docs.fallow.tools/explanations/fallow-vs-linters) | [Reproduce benchmarks](https://github.com/fallow-rs/fallow/tree/main/benchmarks)\n\n``` js\n// fallow-ignore-next-line unused-export\nexport const keepThis = 1;\n\n// fallow-ignore-next-line unused-export, complexity\nexport const publicComplexHelper = (value: number) => value;\n\n// fallow-ignore-file\n// Suppress all issues in this file\n```\n\nUse a comma-separated issue-kind list when one line has multiple findings.\n\nAlso supports JSDoc visibility tags (`/** @public */`\n\n, `/** @internal */`\n\n, `/** @beta */`\n\n, `/** @alpha */`\n\n) to suppress unused export reports for library APIs consumed externally.\n\nSet `ignoreExportsUsedInFile: true`\n\nwhen exported helpers should stay quiet while another symbol in the same file still references them, but should be reported once they become completely unreferenced. The `{ \"type\": true, \"interface\": true }`\n\nobject form is accepted for knip parity; fallow groups type aliases and interfaces under one issue, so both type-kind fields behave identically. References inside the export specifier itself (`export { foo }`\n\n, `export default foo`\n\n) do not count as same-file uses.\n\nfallow uses syntactic analysis -- no type information. This is what makes it fast and deterministic, but findings that require a type-checker (cross-module type narrowing, conditional types, type-level reachability) are 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[Getting started](https://docs.fallow.tools)[Configuration reference](https://docs.fallow.tools/configuration/overview)[CI integration guide](https://docs.fallow.tools/integrations/ci)[Migrating from knip](https://docs.fallow.tools/migration/from-knip)[Fallow compliance happy path](https://github.com/fallow-rs/fallow/blob/main/docs/fallow-compliance.md)[Plugin authoring guide](https://github.com/fallow-rs/fallow/blob/main/docs/plugin-authoring.md)\n\nMissing a framework plugin? Found a false positive? [Open an issue](https://github.com/fallow-rs/fallow/issues).\n\n```\ncargo build --workspace && cargo test --workspace\n```\n\nMIT", "url": "https://wpnews.pro/news/fallow-deterministic-codebase-intelligence-for-typescript-and-javascript", "canonical_source": "https://github.com/fallow-rs/fallow", "published_at": "2026-06-15 10:46:29+00:00", "updated_at": "2026-06-15 11:16:51.462296+00:00", "lang": "en", "topics": ["developer-tools"], "entities": ["Fallow"], "alternates": {"html": "https://wpnews.pro/news/fallow-deterministic-codebase-intelligence-for-typescript-and-javascript", "markdown": "https://wpnews.pro/news/fallow-deterministic-codebase-intelligence-for-typescript-and-javascript.md", "text": "https://wpnews.pro/news/fallow-deterministic-codebase-intelligence-for-typescript-and-javascript.txt", "jsonld": "https://wpnews.pro/news/fallow-deterministic-codebase-intelligence-for-typescript-and-javascript.jsonld"}}