Audit changed files for dead code, complexity, and duplication. Returns a pass, warn, or fail verdict based on the severity of issues found. Scoping to changed files keeps signal-to-noise high: by default only issues introduced by the current PR or commit fail the gate; inherited findings in touched files are shown as context.Documentation Index
Fetch the complete documentation index at: https://docs.fallow.tools/llms.txt
Use this file to discover all available pages before exploring further.
fallow audit is the PR-time gate. For the initial full-repo cleanup on an existing codebase, start with Adopt Fallow in an existing repo, which walks through fallow, fallow dead-code, fallow dupes, and fallow health, and hands the actual cleanup to an AI agent before turning audit on in CI.Options
Base ref
| Flag | Description |
|---|---|
--base <REF> | Git ref to compare against (e.g., main, HEAD~5, a commit SHA). Alias for --changed-since. Auto-detects the default branch if omitted. |
Diff scoping
| Flag | Description |
|---|---|
--diff-file <PATH> | Path to a pre-computed unified diff (e.g. git diff --unified=0 main...HEAD > /tmp/pr.diff). When supplied, the hot-path-touched runtime-coverage verdict is scoped to lines actually inside the diff, not just whole files. Mirrors fallow health --diff-file. Falls back to the FALLOW_DIFF_FILE env var. The bundled GitHub Action and GitLab CI template pre-compute the diff and pass it automatically; you only need this flag for non-PR pipelines or local invocations. |
Gate
| Flag | Description |
|---|---|
--gate new-only | Default. Compare against the base ref and fail only on findings introduced by the current changeset. Inherited findings are reported in JSON attribution and annotated with introduced: false. |
--gate all | Strict mode. Fail on every finding in changed files, including findings inherited from the base ref. Skips the extra base-snapshot attribution pass for lower latency. |
new-only, all) and the precedence is CLI flag > config > MCP param > default.
Docs-only-diff fast path
Under--gate new-only, audit normally runs every analysis twice: once on the current tree and once on a temporary worktree at the base ref so it can attribute each finding as introduced or inherited. When every changed file is either a non-behavioral doc (.md, .markdown, .txt, .rst, .adoc) or token-equivalent at the base ref (a comment-only or whitespace-only edit on a .ts/.tsx/.js/.jsx/.mjs/.mts/.cjs/.cts source file), audit reuses the current run’s findings as the base snapshot, classifies everything as inherited, and skips the second worktree analysis entirely. Common case: docs-only PRs and formatter sweeps complete in roughly one analysis pass instead of two.
Run fallow audit --base main --gate new-only --performance to see whether the fast path fired: the JSON output includes base_snapshot_skipped: true|false and the human output prints the same on stderr. The optimization activates automatically whenever every entry in the diff qualifies; there is no flag to opt in or out.
Output
| Flag | Description |
|---|---|
-f, --format <FORMAT> | Output format: human (default), json, sarif, compact, markdown, codeclimate, gitlab-codequality, pr-comment-github, pr-comment-gitlab, review-github, review-gitlab |
-q, --quiet | Suppress progress and status output on stderr |
--explain | Add metric explanations in JSON output (_meta objects with docs links) |
Scoping
| Flag | Description |
|---|---|
-w, --workspace <NAME> | Scope to a single workspace package |
--production | Exclude test/story/dev files (applies to dead-code, health, and dupes) |
--production-dead-code | Production mode for dead-code only. See global flags. |
--production-health | Production mode for health only. |
--production-dupes | Production mode for duplication only. |
Thresholds
| Flag | Description |
|---|---|
--max-crap <N> | Maximum CRAP score before the audit fails (default: 30.0, configurable via health.maxCrap). Functions meeting or exceeding this score contribute to the fail verdict alongside dead-code and complexity findings. Pair with --coverage for accurate per-function CRAP; without Istanbul data fallow estimates coverage from the module graph. |
--coverage <PATH> | Path to Istanbul-format coverage data (coverage-final.json) for accurate per-function CRAP scores in the health sub-analysis. Also configurable via FALLOW_COVERAGE. Same format and semantics as fallow health --coverage; without it, fallow falls back to the static-estimate model. Relative paths resolve against --root. |
--coverage-root <PATH> | Absolute prefix to strip from file paths in coverage data before prepending the project root. Use when coverage was generated under a different checkout root in CI or Docker (e.g., /home/runner/work/myapp on GitHub Actions). |
Baselines
| Flag | Description |
|---|---|
--dead-code-baseline <PATH> | Baseline file produced by fallow dead-code --save-baseline. Dead-code issues present in the baseline are excluded from the verdict. |
--health-baseline <PATH> | Baseline file produced by fallow health --save-baseline. Complexity findings present in the baseline are excluded from the verdict. |
--dupes-baseline <PATH> | Baseline file produced by fallow dupes --save-baseline. Clone groups present in the baseline are excluded from the verdict. |
--baseline / --save-baseline flags are rejected on audit (exit 2) because audit runs three analyses with incompatible baseline formats. Use the per-analysis flags above, or configure defaults in .fallowrc.json:
.fallow/, because fallow init adds that directory to .gitignore for machine-local cache.
CLI flags override config. Baselines are a no-op if unset; any subset (e.g. dead-code only) is allowed.
Verdict
| Verdict | Exit code | When | What to do |
|---|---|---|---|
| pass | 0 | No introduced issues in changed files (new-only) or no issues at all (all) | Ship it. |
| warn | 0 | Issues found, all warn-severity | CI passes, but consider fixing before they become errors. |
| fail | 1 | Error-severity issues found | Fix the reported issues before merging. |
| error | 2 | Runtime error (invalid ref, not a git repo, config error) | Check the error message. In JSON format, emits {"error": true, "message": "...", "exit_code": 2}. |
fallow health config (defaults: cyclomatic 20, cognitive 15). Duplication is a warning unless a --threshold is configured. With the default new-only gate, inherited error-severity findings can appear in the report while the audit exits 0.
Inline suppression comments (
// fallow-ignore-next-line) work in audit. Findings in changed files are suppressed the same way as in fallow dead-code.JSON contract: which fields are severity-aware
Audit emits three counts that look interchangeable but answer different questions. CI integrations and downstream consumers must gate on the right one:| Field | What it counts | Severity-aware? |
|---|---|---|
verdict | Overall outcome (pass / warn / fail) honoring per-rule severity from .fallowrc.json | Yes |
attribution.dead_code_introduced, attribution.complexity_introduced, attribution.duplication_introduced | Findings introduced by the changeset under gate: new-only, regardless of severity | No |
summary.dead_code_issues, summary.complexity_findings, summary.duplication_clone_groups | All findings in changed files (any gate), regardless of severity | No |
Per-finding introduced: true | false | Whether each individual finding was introduced by the changeset | No (severity is per-rule, not per-finding) |
verdict == "fail" (or check the exit code, which mirrors the verdict). Counting introduced findings re-introduces the bug command: audit was designed to fix: a project with unused-exports: warn would fail CI on every PR that introduces a warn-tier finding, even though the verdict correctly says warn (“do not fail”). The official GitHub Action and GitLab CI template already do this; third-party wrappers should mirror the same contract.
Rule of thumb for AI agents: read verdict first to know whether the run passed or failed, then read attribution for new-vs-inherited counts and walk the per-category finding arrays for actionable details. Use introduced: true to filter to changes the current PR is responsible for.
Examples
Example output
$ fallow audit (pass)
$ fallow audit (warn)
■ summary line appears only on warn verdicts, before any detail sections. It is suppressed with --quiet.
$ fallow audit (fail)
How it works
- Resolve base ref: uses
--baseif provided, otherwise auto-detects the default branch (git symbolic-ref refs/remotes/origin/HEAD→main→master). Hard-errors if no base can be determined. - Find changed files: runs
git diff --name-only <base>...HEAD(three-dot diff, showing changes since the merge base). - Run three analyses scoped to changed files: dead code, complexity, duplication.
- Compute attribution: with
new-only, runs the same analyses at the base ref and compares structural issue keys. - Compute verdict:
new-onlygates only introduced findings;allgates every finding in changed files without the extra base-snapshot analysis.
JSON output
$ fallow audit --format json
Key fields
| Field | Type | Description |
|---|---|---|
verdict | "pass" | "warn" | "fail" | The audit result. Use this for CI gates. |
changed_files_count | integer | Number of files changed between base and HEAD |
base_ref | string | The git ref used for comparison |
summary.dead_code_issues | integer | Total dead code issues in changed files |
summary.dead_code_has_errors | boolean | Whether any dead code issues have error severity |
summary.complexity_findings | integer | Functions exceeding complexity thresholds |
summary.max_cyclomatic | integer | null | Highest cyclomatic complexity found (null if none) |
summary.duplication_clone_groups | integer | Clone groups involving changed files |
attribution.*_introduced | integer | Findings whose structural key did not exist at the base ref. With gate=all, these stay 0 because audit skips the base-snapshot attribution pass. |
attribution.*_inherited | integer | Findings already present at the base ref. With gate=all, these stay 0 because audit skips the base-snapshot attribution pass. |
dead_code.*[].introduced | boolean | Present in audit JSON sub-results when base attribution is available. true means the finding is new relative to the base ref. |
dead_code, complexity, and duplication sub-objects contain full results in the same format as fallow dead-code, fallow health, and fallow dupes respectively. These are omitted when no files changed. In audit output, individual findings and clone groups include an optional introduced boolean when the base ref comparison is available.
On exit code 2 (runtime error), JSON format emits
{"error": true, "message": "...", "exit_code": 2} to stdout instead of the audit envelope.MCP tool
Theaudit MCP tool wraps fallow audit --format json --quiet --explain:
Example request
base is not specified. Set gate to all for strict mode. The response always includes _meta explanatory metadata (the MCP wrapper enables --explain by default). Returns the same JSON envelope as the CLI.
audit creates a temporary git worktree to compare against the base ref. When the current checkout has node_modules, audit links it into the base worktree so tsconfig extends chains into installed packages and path aliases resolve like the working tree. The worktree is removed on normal exit. If the process is force-killed, run git worktree prune to clean up stale .git/worktrees/fallow-audit-base-* entries.
See MCP integration for setup instructions.
See also
Dead code analysis
Full dead code analysis with issue-type filters.
Health analysis
Complexity metrics, file scores, hotspots, and targets.
MCP integration
Use fallow tools from AI coding agents.