fallow 3.31.0 → 3.33.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -55,16 +55,18 @@ fallow dead-code | grep "unused"
55
55
  fallow dead-code --format json --quiet
56
56
  ```
57
57
 
58
- The `--quiet` flag suppresses progress bars on stderr. Without it, stderr output may interfere with stdout parsing.
58
+ The `--quiet` flag suppresses progress bars on stderr. Keep stderr separate from stdout when parsing JSON.
59
59
 
60
60
  ---
61
61
 
62
- ## `--changed-since` Shows Only New Issues
62
+ <a id="--changed-since-shows-only-new-issues"></a>
63
63
 
64
- The `--changed-since` flag limits analysis to files modified since a git ref. It only reports issues in those files, not all issues in the project. Works with both `dead-code` and `dupes`.
64
+ ## `--changed-since` Scopes Findings to Changed Files
65
+
66
+ The `--changed-since` flag scopes findings to files modified since a git ref. It works with both `dead-code` and `dupes`. Existing findings in those files can remain; dead-code dependency findings remain project-wide. Use `fallow audit --gate new-only` to distinguish introduced findings from inherited ones.
65
67
 
66
68
  ```bash
67
- # This only shows issues in files changed since main
69
+ # File-scoped findings are limited to files changed since main
68
70
  fallow dead-code --format json --quiet --changed-since main
69
71
 
70
72
  # Same for duplication, only clone groups involving changed files
@@ -109,10 +111,10 @@ fallow dead-code --format json --quiet
109
111
 
110
112
  ## Syntactic Analysis: No TypeScript Compiler
111
113
 
112
- Fallow uses Oxc for pure syntactic analysis. It does not run the TypeScript compiler. This means:
114
+ Default analysis uses Oxc for syntactic references. The optional `--type-aware` mode adds TypeScript checker evidence. Syntactic analysis has these limits:
113
115
 
114
116
  - **Fully dynamic imports** (`import(variable)`) are not resolved. Only static strings, template literals with static prefixes, `import.meta.glob`, and `require.context` patterns
115
- - **Value-level type narrowing** is not performed. Fallow can't know that `if (x instanceof Foo)` means `Foo` is "used"
117
+ - **General type narrowing** is outside syntactic analysis. Fallow does recognize `if (x instanceof Foo)` guards and credits member calls on `x` as uses of `Foo` members
116
118
  - **Conditional exports** based on runtime values are not analyzed
117
119
  - **Function overload signatures are deduplicated**: TypeScript function overloads (multiple signatures for the same function name) are merged into a single export. They are not reported as separate unused exports
118
120
 
@@ -151,7 +153,7 @@ export * from './utils';
151
153
  import { helper } from './index'; // Resolves through the chain
152
154
  ```
153
155
 
154
- If an export IS flagged as unused despite being in a barrel file, it means no downstream consumer actually imports it. The barrel file re-exports it, but nobody uses it from there.
156
+ A re-export alone does not prove that an export is used. If Fallow reports an export from a barrel, trace its consumers before removal. Dynamic imports and external callers may be outside static analysis.
155
157
 
156
158
  ---
157
159
 
@@ -163,10 +165,10 @@ If an export IS flagged as unused despite being in a barrel file, it means no do
163
165
  | 1 | Error-severity issues found | Review findings |
164
166
  | 2 | Runtime error (`fix` without `--yes` in non-TTY, invalid config) | Fix config or add `--yes` |
165
167
 
166
- Exit code 1 is triggered by issues with `"error"` severity in the rules config. Without a rules section, all issue types default to `"error"`. Use the rules system to control which issues fail CI:
168
+ Error-severity findings can trigger exit code 1. Default severity varies by rule: some rules default to `"warn"` or `"off"`. Use the rules system to control which findings fail CI:
167
169
 
168
170
  ```jsonc
169
- // Only fail on unused files and deps, warn on everything else
171
+ // Warn on unused exports and types; other rules keep their defaults
170
172
  {
171
173
  "rules": {
172
174
  "unused-files": "error",
@@ -177,6 +179,8 @@ Exit code 1 is triggered by issues with `"error"` severity in the rules config.
177
179
  }
178
180
  ```
179
181
 
182
+ Exit code 1 always means that an enforced gate failed. A load warning or a workspace diagnostic (for example `node-modules-missing` or a tsconfig `extends` that does not resolve) never changes the exit code. The only exception is `source-parse-degraded` with `--fail-on-parse-error`. To find the gate, read `gate_outcomes` in the JSON output and look for an entry with `status: "fail"` and `enforced: true`. Under `--quiet` and in every machine format, fallow also prints one stderr line that names the failed gates, for example `[X] Exit code 1: gate health-findings (3 at or above error) failed.` On `health`, the `complexity-*` rules default to `error`, so each complexity finding fails the run. Set them to `warn` or pass `--report-only` to report without a failure.
183
+
180
184
  ---
181
185
 
182
186
  ## `--fail-on-issues` Promotes Warn to Error
@@ -209,13 +213,13 @@ fallow dead-code --format json --quiet --save-baseline fallow-baselines/dead-cod
209
213
  fallow dead-code --format json --quiet --baseline fallow-baselines/dead-code.json --fail-on-issues
210
214
  ```
211
215
 
212
- Commit the baseline file to your repo. Update it periodically as you fix existing issues.
216
+ Commit the baseline file to your repo. When a fix removes a finding, its entry stays in the file until you prune it. `fallow baselines prune` removes the entries that match no current finding and never adds an entry, so it is safe to run after each fix. It reads the paths in `audit.deadCodeBaseline`, `audit.healthBaseline` and `audit.dupesBaseline`.
213
217
 
214
218
  ---
215
219
 
216
220
  ## Duplication Modes Affect What's Detected
217
221
 
218
- The detection mode significantly affects results. Choose based on your needs:
222
+ Each detection mode normalizes different syntax. Choose the mode that fits the comparison:
219
223
 
220
224
  ```bash
221
225
  # strict: exact token match only
@@ -237,6 +241,10 @@ fallow dupes --format json --quiet --mode semantic
237
241
 
238
242
  `semantic` mode produces the most findings but may include false positives where similar structure is coincidental.
239
243
 
244
+ Use `--near` separately when you want function-level clones with small inserted,
245
+ removed, or changed regions. Exact detection still follows `--mode`; near
246
+ detection uses semantic shingles and reports a `similarity` value.
247
+
240
248
  ---
241
249
 
242
250
  ## Workspace Flag Scopes Output, Not Analysis
@@ -333,7 +341,7 @@ If you use utility decorators that DO NOT imply reflective use (Playwright's `@s
333
341
 
334
342
  Conservative semantics: a method carrying any decorator NOT in the list still gets skipped. So `@step` + `@Inject` on the same method stays treated as framework-managed. Matching rule: entries containing `.` (`"decorators.log"`) match the full dotted path; bare entries (`"step"` or `"decorators"`) match the leftmost segment, so a single bare `"decorators"` entry collapses an entire `@decorators.*` namespace. Both `"@step"` and `"step"` round-trip equivalently. Unmatched entries (a decorator name in the config that never appears in your codebase) surface as a one-time warning at end of run.
335
343
 
336
- The default empty list preserves today's skip-all behavior, so existing NestJS / Angular / TypeORM projects see no change.
344
+ With the default empty list every decorated method is treated as framework-managed, which is what NestJS, Angular, and TypeORM projects need.
337
345
 
338
346
  ### Angular `@Input()` / `@Output()` are still covered by the component rules
339
347
 
@@ -654,14 +662,14 @@ Both require a `GITLAB_TOKEN` CI/CD variable (project access token with `api` sc
654
662
  `fallow license refresh` and `fallow license activate --trial` can fail with a backend error. The CLI always appends the raw HTTP status and the backend error code after the human hint, so scripts can grep for the code without parsing prose:
655
663
 
656
664
  ```
657
- fallow license refresh: your stored license is too stale to refresh. Reactivate with: fallow license activate --trial --email <addr> (HTTP 401, code token_stale)
665
+ fallow license refresh: your stored license is too stale to refresh: set FALLOW_API_KEY to a full-access key and run `fallow license refresh` again (generate one at https://fallow.cloud/settings#api-keys) (HTTP 401, code token_stale)
658
666
  ```
659
667
 
660
668
  Stable codes the CLI surfaces today:
661
669
 
662
670
  | Code | Operation | Meaning |
663
671
  |------|-----------|---------|
664
- | `token_stale` | `refresh` | Stored JWT is more than 45 days past its `exp`. Reactivate. |
672
+ | `token_stale` | `refresh` | Stored JWT is more than 45 days past its `exp`. Surfaced only when no full-access API key was available to retry with. |
665
673
  | `invalid_token` | `refresh` | Stored JWT is missing required claims (e.g. `sub`). Reactivate. |
666
674
  | `unauthorized` | `refresh` or `trial` | Auth failed. Reactivate. |
667
675
  | `rate_limit_exceeded` | `trial` | Trial endpoint is capped at 5 per hour per IP. Wait or use a different network. |
@@ -30,11 +30,11 @@ The `generated:issue-types` table below is regenerated from `fallow schema` by s
30
30
  | `duplicate-export` | `--duplicate-exports` | - | `// fallow-ignore-file duplicate-export` | Same symbol exported from multiple modules |
31
31
  | `circular-dependency` | `--circular-deps` | - | `// fallow-ignore-next-line circular-dependency` | Import cycles in the module graph |
32
32
  | `re-export-cycle` | `--re-export-cycles` | - | `// fallow-ignore-file re-export-cycle` | Barrel files re-exporting from each other in a loop (`kind: "multi-node"`) or a barrel re-exporting from itself (`kind: "self-loop"`). Chain propagation through the loop is a structural no-op so imports through any member may silently come up empty. Default `warn`. Distinct from `circular-dependencies` (runtime cycles, sometimes intentional). File-scoped suppression only: `// fallow-ignore-file re-export-cycle` on any member breaks the cycle. |
33
- | `package-cycle` | `--package-cycles` | - | `// fallow-ignore-next-line package-cycle` | Two or more workspace packages import each other in a loop, so they cannot be built in dependency order. Edges are resolved imports, not declared `package.json` dependencies. Imports from test, spec, story, fixture and tooling config files do not count. Type-only imports count, and each hop in `edges` has a `type_only` flag. Default `warn`. Requires a workspace with two or more packages. A suppression removes one import; the cycle goes away when every import on one hop is suppressed. `group_truncated: true` means the package group has more cycles than listed. `--changed-since` and diff scope use the example import of each hop (`edges[].path`) |
33
+ | `package-cycle` | `--package-cycles` | - | `// fallow-ignore-next-line package-cycle` | Two or more workspace packages import each other in a loop, so they cannot be built in dependency order. Edges are resolved imports, not declared `package.json` dependencies, so a package cycle can exist without a file-level cycle. Imports from test, spec, story, fixture and tooling config files do not count. Each finding lists `packages` in cycle order and one example import per hop in `edges`. Type-only imports count, and each hop has a `type_only` flag (a type-only hop still matters for declaration builds). Default `warn`. Requires a workspace with two or more packages. A suppression removes one import; the cycle goes away when every import on one hop is suppressed. `group_truncated: true` means the package group has more cycles than listed. `--changed-since` and diff scope use the example import of each hop (`edges[].path`). |
34
34
  | `boundary-violation` | `--boundary-violations` | - | `// fallow-ignore-next-line boundary-violation` | Imports crossing architecture zone boundaries. Presets: `layered`, `hexagonal`, `feature-sliced`, `bulletproof`; `autoDiscover` can create one zone per feature directory; per-rule `allowTypeOnly: [zones]` admits `import type` / `export type` crossings while still blocking value imports. Named and default imports through a barrel are judged against the zone of the origin module (`to_path`), and `via_path` names the barrel. Optional sections: `boundaries.coverage.requireAllFiles` reports unzoned source files (`allowUnmatched` globs exempt intentional ones), and `boundaries.calls.forbidden` bans callee patterns per zone (segment-aware and import-resolved, so `child_process.*` covers `node:child_process` named/namespace/default imports; direct callees only, zoned files only). The whole family shares the `boundary-violation` rule and suppression token (`boundary-call-violation` and `boundary-call-violations` accepted as aliases); start the rule at `warn` for a staged rollout |
35
35
  | `boundary-coverage` | `--boundary-violations` | - | `// fallow-ignore-file boundary-violation` | Source file matches no configured architecture boundary zone; Requires boundaries.coverage.requireAllFiles |
36
36
  | `boundary-call-violation` | `--boundary-violations` | - | `// fallow-ignore-next-line boundary-call-violation` | Zoned file calls a callee its zone forbids; Requires boundaries.calls.forbidden patterns |
37
- | `policy-violation` | `--policy-violations` | - | `// fallow-ignore-next-line policy-violation` | Calls, imports, or catalogue-derived effects banned by a declarative rule pack (`rulePacks` config key lists standalone JSON/JSONC files of `banned-call`, `banned-import`, and `banned-effect` rules; pure data, no project code executes). Findings identified as `<pack>/<rule-id>`. Default `warn` master; per-rule `severity` overrides per finding and the exit gate reads the effective severity. Invalid or missing packs fail config load with exit 2. `fallow rule-pack-schema` prints the pack JSON Schema. Use the scoped token to suppress one rule; bare `policy-violation` still covers every pack rule on the line or file. |
37
+ | `policy-violation` | `--policy-violations` | - | `// fallow-ignore-next-line policy-violation` | Calls, imports, exports, catalogue-derived effects, and graph-resolved GDP proof producer ownership checked by declarative rule packs (`rulePacks` lists standalone JSON/JSONC files; pure data, no project code executes). Rule kinds: `banned-call`, `banned-import`, `banned-export`, `banned-effect`, and `gdp-proof-producer`. GDP rules trace `@gdp-ts/core` proof producers through unambiguous re-exports and require project-relative `allowedFiles`, optionally scoped by exact static `proofKinds`; they do not prove runtime authorization. Findings identified as `<pack>/<rule-id>`. Default `warn` master; per-rule `severity` overrides per finding and the exit gate reads the effective severity. Invalid or missing packs fail config load with exit 2. `fallow rule-pack-schema` prints the pack JSON Schema. Use `policy-violation:<pack>/<rule-id>` to suppress one rule; bare `policy-violation` covers every pack rule on the line or file. |
38
38
  | `stale-suppression` | `--stale-suppressions` | - | - | `fallow-ignore` comments or `@expected-unused` JSDoc tags that no longer match any issue |
39
39
  | `missing-suppression-reason` | `--stale-suppressions` | - | - | Suppression comment omits a required reason |
40
40
  | `unused-catalog-entry` | `--unused-catalog-entries` | yes | - | `pnpm-workspace.yaml` entries no workspace package.json references via `catalog:` (default `warn`) |
@@ -47,6 +47,7 @@ The `generated:issue-types` table below is regenerated from `fallow schema` by s
47
47
  | `misplaced-directive` | - | - | `// fallow-ignore-next-line misplaced-directive` | "use client" / "use server" directive is not in the leading position and is ignored; Requires the project to declare next |
48
48
  | `unprovided-inject` | `--unprovided-injects` | - | `// fallow-ignore-next-line unprovided-inject` | inject() / getContext() reads a key that no provide() / setContext() supplies |
49
49
  | `unrendered-component` | `--unrendered-components` | - | `// fallow-ignore-next-line unrendered-component` | A Vue / Svelte component is reachable through a barrel but rendered nowhere |
50
+ | `absent-component-prop` | `--absent-component-props` | - | `// fallow-ignore-next-line absent-component-prop` | Known reachable callers omit an optional prop consumed inside its component; Opt-in manual review candidate; defaults to off. Inspect caller evidence and defaults before changing the component. No automatic fix. |
50
51
  | `unused-component-prop` | `--unused-component-props` | - | `// fallow-ignore-next-line unused-component-prop` | A Vue defineProps prop or React component prop is referenced nowhere in its own component |
51
52
  | `unused-component-emit` | `--unused-component-emits` | - | `// fallow-ignore-next-line unused-component-emit` | A Vue <script setup> defineEmits event is emitted nowhere in its own component |
52
53
  | `unused-component-input` | `--unused-component-inputs` | - | `// fallow-ignore-next-line unused-component-input` | An Angular @Input() / signal input() / model() is read nowhere in its own component (class body or template); needs `@angular/core` dep |
@@ -14,7 +14,8 @@ When using fallow via MCP (`fallow-mcp`), the following tools are available:
14
14
  | `code_execute` | composition | free | - | `code`, `timeout_ms`, `max_output_bytes` | Bounded read-only Code Mode for composing multiple fallow analysis calls in one JavaScript snippet. The snippet receives `{ fallow, root }`, returns JSON-serializable data, and can call read-only helpers such as `fallow.projectInfo`, `fallow.audit`, `fallow.checkHealth`, and `fallow.run(tool, params)` for the same allowlist. `fallow.all(requests)` fans out independent calls in one go: pass `[{ tool, params }, ...]` and get back a positionally aligned array of `{ ok: true, value }` or `{ ok: false, error }`, so one failing element never hides the rest. Host calls are memoized for the duration of one snippet, so repeating the same tool with the same params (key order does not matter) is served from cache, spends no `max_host_calls` slot and no output budget, and is reported in `calls[]` with `cache_hit: true`; a call refused before dispatch (unknown tool, malformed params) spends no slot either, and `limits.max_rejected_host_calls` bounds how many of those the response records. Similar-code is excluded because Code Mode is capped at 30 seconds; use standalone `find_similar_code` and `inspect_similar_code`, which have dedicated 15-minute timeouts. Mutating fix tools are not exposed, and a host call that passes `save_baseline`, `save_regression_baseline` or `save_snapshot` is refused with the name of the standalone tool to call. The sandbox has no filesystem, network, imports, `process`, `require`, `Deno`, `Bun`, or shell access, and no dynamic code compilation: `eval`, `Function`, and the async and generator function constructors are removed, including the `constructor` route reachable through function prototypes. Params: `code`, optional `root`, `timeout_ms` (capped at 30000), and `max_output_bytes` (capped at 4000000). `max_output_bytes` bounds two separate things: the total fallow JSON host calls read, shared across a `fallow.all` fan-out rather than granted per element, and the serialized snippet result. An oversized result is refused with `ok:false`, `truncated:true`, `result_bytes`, and a short `result_preview` in place of the value, never returned whole, so return a projection rather than a whole report. |
15
15
  | `analyze` | analysis | free | `fallow dead-code --format json --quiet` | `issue_types`, `production`, `workspace`, `baseline`, `group_by`, `file` | Full dead code analysis (unused files/exports/types/dependencies/members + circular dependencies + re-export cycles (barrel files that form a structural loop, silently breaking re-exports) + package cycles (workspace packages that import each other in a loop) + boundary violations + rule-pack policy violations (banned calls, imports, and catalogue-derived effects declared via the `rulePacks` config key) + stale suppressions). Private type leaks are an opt-in API hygiene check via `issue_types: ["private-type-leaks"]`. Deprecated exports that still have consumers are an opt-in migration sweep via `issue_types: ["deprecated-exports-in-use"]`. Set `boundary_violations: true` as a convenience alias for `issue_types: ["boundary-violations"]`. Set `group_by` to `"owner"`, `"directory"`, `"package"`, or `"section"` to partition results. The `section` mode reads GitLab CODEOWNERS `[Section]` headers and emits `owners` metadata per group |
16
16
  | `check_changed` | analysis | free | `fallow dead-code --changed-since <ref> --format json --quiet` | `since`, `baseline`, `fail_on_regression` | Incremental analysis of files changed since a git ref |
17
- | `security_candidates` | analysis | free | `fallow security --format json --quiet` | `gate`, `surface`, `changed_since`, `paths` | Unverified local security candidates, not confirmed vulnerabilities (`fallow security --format json`). Read `security_findings[]` for category, CWE, severity, evidence, trace, optional `reachability`, blind-spot counters, and optional `unresolved_callee_diagnostics` samples for dynamic callee follow-up. `severity` is a review-priority tier, not a verified vulnerability verdict. Each finding also carries an agent-actionable `candidate` (`source_kind`/`sink`/`boundary`), where URL-category sinks may include `url_shape` (`fixed-origin-dynamic-path` or `dynamic-origin`), an optional `taint_flow` source-to-sink triple, and a stable `finding_id` (equal to the SARIF fingerprint) for cross-run correlation; there is no `impact` field (deciding exploitability is the agent's job). Set `surface: true` to include top-level `attack_surface[]` entries with defensive-boundary prompts for a verifier. Set `gate` to `new` for changed-line candidates or `newly-reachable` for candidates that became reachable from entry points; `newly-reachable` requires `changed_since`. `reachability.untrusted_source_trace` is module-level import context only and does not prove value flow; `reachability.taint_confidence` tiers each reachable candidate as `arg-level` (sink argument traces to a same-module source read, strong) or `module-level` (only the module is import-reachable from a source, weak), so tier from this field instead of the evidence text. Verify trace, reachability context, severity, and evidence before editing code. Supports `root`, `config`, `workspace`, `paths`, `changed_since`, `changed_workspaces`, `surface`, `gate`, `no_cache`, and `threads`; `paths` forwards repeated `fallow security --file` filters for finding anchors, trace hops, untrusted-source reachability trace hops, and unresolved-callee diagnostics. See <https://docs.fallow.tools/cli/security-agent-verification> for the verifier packet and verdict recipe. Inherits `FALLOW_DIFF_FILE` from the server environment for line-level diff scoping; raise `FALLOW_TIMEOUT_SECS` for large repos. |
17
+ | `check_architecture` | analysis | free | `fallow architecture --format json --quiet` | `cycles`, `boundaries`, `policy`, `baseline`, `group_by`, `file` | Architecture check: import cycles, boundary violations, and rule-pack policy violations in the `architecture` envelope, each with a stable finding_id |
18
+ | `security_candidates` | analysis | free | `fallow security --format json --quiet` | `gate`, `surface`, `changed_since`, `paths` | Unverified local security candidates, not confirmed vulnerabilities (`fallow security --format json`). Read `security_findings[]` for category, CWE, severity, evidence, trace, optional `reachability`, blind-spot counters, and optional `unresolved_callee_diagnostics` samples for dynamic callee follow-up. `severity` is a review-priority tier, not a verified vulnerability verdict. Each finding also carries an agent-actionable `candidate` (`source_kind`/`sink`/`boundary`), where URL-category sinks may include `url_shape` (`fixed-origin-dynamic-path` or `dynamic-origin`), an optional `taint_flow` source-to-sink triple, and a stable `finding_id` (equal to the SARIF fingerprint) for cross-run correlation; there is no `impact` field (deciding exploitability is the agent's job). Set `surface: true` to include top-level `attack_surface[]` entries with defensive-boundary prompts for a verifier. Set `gate` to `new` for changed-line candidates or `newly-reachable` for candidates that became reachable from entry points; `newly-reachable` requires `changed_since`. `reachability.untrusted_source_trace` is module-level import context only and does not prove value flow; `reachability.taint_confidence` tiers each reachable candidate as `arg-level` (sink argument traces to a same-module source read, strong) or `module-level` (only the module is import-reachable from a source, weak), so tier from this field instead of the evidence text. Verify trace, reachability context, severity, and evidence before editing code. Supports `root`, `config`, `workspace`, `paths`, `changed_since`, `changed_workspaces`, `surface`, `gate`, `no_cache`, and `threads`; `paths` forwards repeated `fallow security --file` filters for finding anchors, trace hops, untrusted-source reachability trace hops, and unresolved-callee diagnostics. See <https://fallow.tools/docs/cli/security-agent-verification/> for the verifier packet and verdict recipe. Inherits `FALLOW_DIFF_FILE` from the server environment for line-level diff scoping; raise `FALLOW_TIMEOUT_SECS` for large repos. |
18
19
  | `find_similar_code` | analysis | free | `fallow similar-code --format json --quiet` | `threshold`, `min_lines`, `top`, `changed_since`, `paths` | Find unverified semantically similar function candidates with the exact pinned local model. Discovery is read-only and never authorizes or performs model setup. Scoped output materializes the exact admitted files once in `generation.scope.paths` as provenance. Ask the user to run `fallow similar-code setup --local` when setup is missing. Cold local inference has a dedicated 15-minute subprocess window. |
19
20
  | `inspect_similar_code` | trace | free | `fallow similar-code inspect <candidate-id> --candidates <report.json> --format json --quiet` | `candidate_id`, `snapshot` | Inspect one exact unverified candidate without rerunning provider retrieval or global ranking. Pass `candidate_id` plus a bounded typed snapshot containing the unchanged discovery `schema_version`, `generation`, selected `candidate`, `completion`, and `diagnostics`. Current source is re-extracted and both endpoint hashes must still match, so stale source fails closed. Keep candidate worthiness, behavioral equivalence, and refactor safety as separate verdicts, and abstain when evidence is incomplete. |
20
21
  | `inspect_target` | analysis | free | `fallow inspect --format json --quiet` | `target`, `production`, `include_churn` | Compose one evidence bundle for a file or exported symbol. File targets use `target: { type: "file", file }`; symbol targets use `target: { type: "symbol", file, export_name }`. Returns `kind: "inspect_target"`, normalized target identity, `trace_file`, optional `trace_export`, file-scoped dead-code actions, duplication groups filtered to the file, complexity findings filtered to the file, and security candidates scoped to the file. Set `include_churn: true` to add target-level git churn from the health hotspot subsystem. Churn is off by default, and unavailable git history or analysis failures remain explicit section states. Evidence sections carry `status` and `scope`; symbol targets warn when supporting evidence is file-scoped. Supports `root`, `config`, `production`, `workspace`, `include_churn`, `no_cache`, and `threads`; `production` applies to trace, dead-code, and health evidence only. Raise `FALLOW_TIMEOUT_SECS` for large repos. |
@@ -49,15 +50,21 @@ When using fallow via MCP (`fallow-mcp`), the following tools are available:
49
50
  | `trace_import_path` | trace | free | `fallow trace --path <from> <to> --format json --quiet` | `from`, `to` | Trace the shortest import path between two modules, hop by hop |
50
51
  | `trace_error` | trace | free | `fallow trace-error - --format json --quiet` | `trace` | Resolve a runtime stack trace's frames against the project graph |
51
52
  | `impact_closure` | trace | free | `fallow dead-code --impact-closure <path> --format json --quiet` | `path` | Trace the transitive affected-but-not-in-diff set and coordination gaps for one file. Supports `root`, `config`, `production`, `workspace`, `no_cache`, and `threads`. Use as review-planning evidence for a file contract, not proof that affected files are wrong |
52
- | `trace_dependency` | trace | free | `fallow dead-code --trace-dependency <package> --format json --quiet` | `package_name` | Trace where a dependency is imported (`fallow dead-code --trace-dependency PACKAGE --format json`). Required `package_name`. Returns importing files, type-only importers, total import count, `used_in_scripts` (true when invoked from package.json scripts or CI configs), and `is_used` (combined import + script signal; mirrors the unused-deps detector so build tools like `microbundle` or `vitest` are not falsely flagged as unused). Use before removing a dependency or moving between `dependencies` and `devDependencies` |
53
- | `trace_clone` | trace | free | `fallow dupes --trace <file:line> --format json --quiet` | `file`, `line`, `fingerprint`, `near`, `min_occurrences` | Deep-dive a duplicate-code clone group (`fallow dupes --trace <spec> --format json`). Address by exactly one of: `file` + `line` (a source location), or `fingerprint` (a `dup:<id>` from a prior `find_dupes` `clone_groups[].fingerprint`, usually `dup:<8hex>` and widened only on rare report collisions). Returns the matched clone instance plus every clone group containing it; each traced group carries its `fingerprint`, an extract-function `suggestion` with estimated savings, and a best-effort `suggested_name` (omitted when no confident name). Supports `mode`, `near`, `min_tokens`, `min_lines`, `min_occurrences`, `threshold`, `skip_local`, `cross_language`, `ignore_imports`. Use the same `near` value as the originating `find_dupes` call. Use to consolidate duplication when you need exact sibling locations and a refactor target |
53
+ | `trace_dependency` | trace | free | `fallow dead-code --trace-dependency <package> --format json --quiet` | `package_name`, `specifiers`, `sites`, `limit`, `cursor` | Trace where a dependency is imported (`fallow dead-code --trace-dependency PACKAGE --format json`). Required `package_name`. Returns importing files, type-only importers, total import count, `used_in_scripts` (true when invoked from package.json scripts or CI configs), and `is_used` (combined import + script signal; mirrors the unused-deps detector so build tools like `microbundle` or `vitest` are not falsely flagged as unused). Use before removing a dependency or moving between `dependencies` and `devDependencies` |
54
+ | `trace_clone` | trace | free | `fallow dupes --trace <file:line> --format json --quiet` | `file`, `line`, `fingerprint`, `near`, `min_occurrences` | Deep-dive a duplicate-code clone group (`fallow dupes --trace <spec> --format json`). Address by exactly one of: `file` + `line` (a source location), or `fingerprint` (a `dup:<id>` from a prior `find_dupes` `clone_groups[].fingerprint`, usually `dup:<8hex>` and widened only on rare report collisions). Returns the matched clone instance plus every clone group containing it; each traced group carries its `fingerprint`, an extract-function `suggestion` with estimated savings, and a best-effort `suggested_name` (omitted when no confident name). Supports `mode`, `near`, `min_tokens`, `min_lines`, `min_occurrences`, `threshold`, `skip_local`, `ignore_symlinks`, `cross_language`, `ignore_imports`. Use the same `near` value as the originating `find_dupes` call. Use to consolidate duplication when you need exact sibling locations and a refactor target |
54
55
  <!-- generated:mcp-tools:end -->
55
56
 
56
57
  Tool hints: `fix_apply` changes source files and declares `destructiveHint: true`. `analyze`, `check_changed`, `find_dupes` and `check_health` write a baseline, regression baseline or snapshot file when you pass `save_baseline`, `save_regression_baseline` or `save_snapshot`. They declare `readOnlyHint: false` and `destructiveHint: false`, so a host can ask for approval before it runs them. Every other tool declares `readOnlyHint: true`.
57
58
 
58
59
  ## Resource catalogue
59
60
 
60
- Resources are the server's read-only reference channel: compile-time material an agent can list (`resources/list`, `resources/templates/list`) and read (`resources/read`) with no subprocess and no analysis run, cacheable by URI (your client reads them through its own resource tool). Each content item carries the server version in `_meta.fallow_version`; the payload itself is the plain document, so the schema resources are valid strict JSON Schema. Unknown URIs return a structured error whose `data` lists the known URIs (or the nearest issue types); the numeric code is `-32002` on protocol versions before 2026-07-28 and `-32602` from then on, so key on `data`. Every payload is JSON and carries `fallow_version`, so cache by URI and invalidate when the server version changes. The catalogue is static (no `subscribe`, no `listChanged`). Read `fallow://explain/{issue_type}` instead of calling `fallow_explain` when you only need the reference document; `issue_type` accepts the bare id (`unused-export`), the namespaced rule id (`security/sql-injection`), or the CLI filter spelling (`unused-exports`). An unknown URI or issue type returns a structured `resource_not_found` error listing the known URIs or the nearest issue types.
61
+ Resources contain read-only, compile-time reference documents. Use your client's resource tool to list them (`resources/list`, `resources/templates/list`) and read them (`resources/read`). These reads start no subprocess or analysis. The catalogue is static (no `subscribe`, no `listChanged`).
62
+
63
+ Every payload is JSON. Each content item carries the server version in `_meta.fallow_version`. The payload itself is the plain document, so schema resources remain valid strict JSON Schema. Cache by URI and invalidate the cache when the server version changes.
64
+
65
+ An unknown URI or issue type returns a structured `resource_not_found` error. Its `data` lists the known URIs or the nearest issue types. The numeric code is `-32002` on protocol versions before 2026-07-28 and `-32602` from then on, so key on `data`.
66
+
67
+ Read `fallow://explain/{issue_type}` when you need only the reference document. `fallow_explain` provides the tool equivalent. `issue_type` accepts the bare id (`unused-export`), the namespaced rule id (`security/sql-injection`), or the CLI filter spelling (`unused-exports`).
61
68
 
62
69
  <!-- generated:mcp-resources:start -->
63
70
  | Resource | Name | Kind | MIME | Description |
@@ -138,8 +145,14 @@ An entry in `not_found` has no cloud data. Absence is not evidence that the code
138
145
 
139
146
  Most tools accept `root`, `config`, `no_cache`, and `threads` params. Exceptions: `impact` takes only `root`; `code_execute` takes `code`, optional `root`, `timeout_ms`, and `max_output_bytes`. Similar-code file scope is named `paths` on both standalone MCP tools and forwards repeated CLI `--file` flags. MCP subprocesses default to 120 seconds, except similar-code discovery and inspection which default to 15 minutes for cold local inference. `FALLOW_TIMEOUT_SECS` overrides either default. Code Mode does not expose similar-code because its 30-second cap cannot satisfy that contract.
140
147
 
141
- All JSON responses include structured `actions` arrays on every finding (dead code, health, duplication), enabling programmatic fix application or suppression.
148
+ Every dead-code, health, and duplication finding in JSON responses includes a structured `actions` array for programmatic fixes or suppression.
142
149
 
143
150
  `health.thresholdOverrides[]` lets projects keep known legacy functions visible as configured local ceilings instead of hiding them with suppressions. Each entry has `files` globs, optional exact `functions`, one or more of `maxCyclomatic`, `maxCognitive`, `maxCrap`, or `maxUnitSize`, and optional `reason`. Health JSON may include top-level `threshold_overrides[]` entries with `active`, `stale`, `insufficient`, or `no_match` status, and complexity findings that use an override carry `effective_thresholds` plus `threshold_source: "override"`. Each entry also names its `dimension` (`complexity` or `crap`), so one configured override yields one entry per dimension it participates in: group on `override_index` to count configured overrides. `insufficient` means the raised ceiling is still exceeded. An entry's `outstanding[]` lists every dimension the matched unit still breaches after the override applied, which is how an `active` override can sit next to a surviving finding.
144
151
 
145
- `dead-code`, `health`, `dupes`, bare `fallow`, and `audit` JSON output also carry a top-level `next_steps` array of read-only follow-up commands computed from the run's findings: each entry is `{ id, command, reason }`. The `command` is runnable as-is (never a placeholder, never `fix` or any other mutating command); the stable kebab-case `id` (`setup`, `impact-report`, `trace-unused-export`, `trace-deprecated-export`, `trace-clone`, `complexity-breakdown`, `scope-workspaces`, `audit-changed`) maps to a verification step you should run BEFORE acting, for example tracing an export before deleting it. A leading `setup` step (command: `fallow schema`) appears only on unconfigured, non-CI projects with findings and doubles as an onboarding trigger; it disappears after setup or `fallow init --decline`. An at-most-weekly `impact-report` step (command: `fallow impact`) carries the local value digest when impact tracking has non-zero results; it may ride a clean run. When running via MCP, dispatch on the `id` to the matching tool / `code_execute` host call (`trace_export`, `trace_clone`, `check_health` with `complexity_breakdown: true`, `audit`) rather than shelling out the CLI string. The array is deduplicated, capped at three, and omitted when empty; set `FALLOW_SUGGESTIONS=off` to suppress it.
152
+ `dead-code`, `health`, `dupes`, bare `fallow`, and `audit` JSON output may include a top-level `next_steps` array of read-only follow-up commands computed from the run's findings. Each entry is `{ id, command, reason }`. The `command` is runnable as-is (never a placeholder, never `fix` or any other mutating command); the stable kebab-case `id` (`setup`, `impact-report`, `trace-unused-export`, `trace-deprecated-export`, `trace-clone`, `complexity-breakdown`, `scope-workspaces`, `audit-changed`) maps to a verification step to run before acting, for example tracing an export before deleting it.
153
+
154
+ A leading `setup` step (command: `fallow schema`) appears only on unconfigured, non-CI projects with findings and doubles as an onboarding trigger; it disappears after setup or `fallow init --decline`.
155
+
156
+ An at-most-weekly `impact-report` step (command: `fallow impact`) carries the local value digest when impact tracking has non-zero results; it may appear in a clean run.
157
+
158
+ When running via MCP, dispatch on the `id` to the matching tool or `code_execute` host call (`trace_export`, `trace_clone`, `check_health` with `complexity_breakdown: true`, `audit`) rather than shelling out the CLI string. The array is deduplicated, capped at three, and omitted when empty; set `FALLOW_SUGGESTIONS=off` to suppress it.
@@ -1,6 +1,6 @@
1
1
  # Node.js Bindings
2
2
 
3
- When embedding fallow inside a Node.js process (editor extensions, long-running servers, custom tooling), prefer the NAPI bindings over spawning the CLI. Same analysis engine, same JSON envelopes, no subprocess or JSON parsing overhead.
3
+ To embed fallow in a Node.js process, use the NAPI bindings. They use the same analysis engine and JSON envelopes as the CLI without spawning the CLI or parsing its JSON output. Examples include editor extensions, long-running servers, and custom tooling.
4
4
 
5
5
  ```bash
6
6
  npm install @fallow-cli/fallow-node
@@ -15,8 +15,14 @@ const similarCode = await detectSimilarCode({ root: process.cwd(), files: ['src/
15
15
  const health = await computeHealth({ root: process.cwd(), score: true, ownershipEmails: 'handle' });
16
16
  ```
17
17
 
18
- Eight async functions: `detectDeadCode`, `detectCircularDependencies`, `detectBoundaryViolations`, `detectDuplication`, `detectSimilarCode`, `detectFeatureFlags`, `computeComplexity`, `computeHealth`. Each returns the same JSON envelope the CLI emits for `--format json`. `detectSimilarCode` returns a precisely typed `SimilarCodeReport`, including generation provenance, embedding semantics, effective `generation.scope.paths`, completion, skips, cache accounting, diagnostics, and read-only candidate actions. Treat the materialized scope as provenance and preserve the raw report when a candidate may be inspected later. The Node binding exposes discovery only. Use CLI `similar-code inspect --candidates <report.json>` or MCP `inspect_similar_code` with the exact typed candidate snapshot so global retrieval and ranking are not repeated. Its loader resolves and verifies the exact-version local companion; it never downloads the model or authorizes setup. Rejected promises throw a `FallowNodeError` with `message`, `exitCode`, and optional `code`, `help`, `context` fields that mirror the CLI's structured error surface.
18
+ The async functions are `detectDeadCode`, `detectCircularDependencies`, `detectBoundaryViolations`, `detectArchitecture`, `detectDuplication`, `detectSimilarCode`, `detectFeatureFlags`, `computeComplexity`, and `computeHealth`. Each returns the same JSON envelope the CLI emits for `--format json`. `detectArchitecture` returns the `kind: "architecture"` envelope of `fallow architecture`; `detectCircularDependencies` and `detectBoundaryViolations` return the `dead-code` envelope.
19
+
20
+ `detectSimilarCode` returns a precisely typed `SimilarCodeReport` with generation provenance, embedding semantics, effective `generation.scope.paths`, completion, skips, cache accounting, diagnostics, and read-only candidate actions. Treat the materialized scope as provenance. Preserve the raw report when a candidate may be inspected later.
21
+
22
+ `detectSimilarCode` exposes discovery only. Use CLI `similar-code inspect --candidates <report.json>` or MCP `inspect_similar_code` with the exact typed candidate snapshot. This avoids repeating global retrieval and ranking. The loader resolves and verifies the exact-version local companion. It never downloads the model or authorizes setup.
23
+
24
+ Rejected promises throw a `FallowNodeError` with `message`, `exitCode`, and optional `code`, `help`, `context` fields. These fields match the CLI's structured errors.
19
25
 
20
26
  Enum-like fields take lowercase CLI-style literals (`"mild"`, `"cyclomatic"`, `"handle"`, `"low"`). Write-path commands (`fix`, `init`, `hooks install`, `hooks uninstall`, `license activate`, `coverage setup`) are not exposed; use the CLI for those.
21
27
 
22
- See <https://docs.fallow.tools/integrations/node-bindings> for the full field reference.
28
+ See <https://fallow.tools/docs/integrations/node-bindings/> for the full field reference.
@@ -66,7 +66,7 @@ fallow dead-code --format json --quiet
66
66
 
67
67
  ## PR Dead Code Check
68
68
 
69
- Check if a pull request introduces new dead code.
69
+ Report dead-code findings in files changed by a pull request.
70
70
 
71
71
  ### Step 1: Analyze changed files
72
72
 
@@ -74,7 +74,7 @@ Check if a pull request introduces new dead code.
74
74
  fallow dead-code --format json --quiet --changed-since main --fail-on-issues
75
75
  ```
76
76
 
77
- Exit code 1 if the PR introduces new dead code. Exit code 0 if clean.
77
+ With `--fail-on-issues`, reported warn-severity and error-severity findings exit 1. Existing findings in changed files can also fail this check. Use `fallow audit --gate new-only` when the gate should fail only on introduced findings.
78
78
 
79
79
  ### Step 2: If issues found, show specifics
80
80
 
@@ -82,7 +82,7 @@ Exit code 1 if the PR introduces new dead code. Exit code 0 if clean.
82
82
  fallow dead-code --format json --quiet --changed-since main
83
83
  ```
84
84
 
85
- Parse the JSON to list specific files and exports that became unused.
85
+ Parse the JSON to list reported files and exports. This command does not distinguish introduced findings from inherited ones.
86
86
 
87
87
  ---
88
88
 
@@ -334,14 +334,16 @@ git commit -m "chore: add fallow baseline"
334
334
  fallow dead-code --format json --quiet --baseline fallow-baselines/dead-code.json --fail-on-issues
335
335
  ```
336
336
 
337
- ### Step 4: Gradually fix and update baseline
337
+ ### Step 4: Gradually fix and prune the baseline
338
338
 
339
- As you fix existing issues, regenerate the baseline:
339
+ As you fix existing issues, remove their entries from the baseline. Prune removes only the entries that match no current finding, so a new finding stays visible:
340
340
 
341
341
  ```bash
342
- fallow dead-code --format json --quiet --save-baseline fallow-baselines/dead-code.json
342
+ fallow baselines prune --dead-code-baseline fallow-baselines/dead-code.json --format json --quiet
343
343
  ```
344
344
 
345
+ With `audit.deadCodeBaseline`, `audit.healthBaseline` and `audit.dupesBaseline` in the config, `fallow baselines prune` prunes all three files in one run. Use `--save-baseline` only to record new findings on purpose. `fallow hooks install --target git --prune-baselines` runs the prune in the pre-commit hook and stages the pruned files.
346
+
345
347
  ### Duplication baseline
346
348
 
347
349
  Same pattern works for duplication:
@@ -446,7 +448,7 @@ Creates `.fallowrc.json` with mapped settings:
446
448
  hidden, but matching files remain in the module graph; leading `!` exceptions
447
449
  are preserved; multi-source findings stay visible unless every source owner
448
450
  matches)
449
- - knip `ignoreDependencies` → fallow `ignoreDependencies`
451
+ - knip `ignoreDependencies` → fallow `ignoreDependencies` (a regex such as `@org/.+` becomes the glob `@org/*` when the glob matches the same packages; other regexes are skipped with a warning)
450
452
  - knip `ignoreExportsUsedInFile` → fallow `ignoreExportsUsedInFile` (boolean and `{ type, interface }` object form both supported; fallow groups type aliases and interfaces under one issue, so the two type-kind fields behave identically)
451
453
  - Unmappable fields generate warnings with suggestions
452
454
 
@@ -614,7 +616,7 @@ export const dynamicallyUsed = createHandler();
614
616
 
615
617
  ### If the trace shows it's NOT used
616
618
 
617
- The export is genuinely unused. Consider removing it or marking it as intentionally kept:
619
+ The trace found no static use. Check dynamic imports, reflection, and external callers before removal. If the export must remain, mark it as intentionally kept:
618
620
 
619
621
  ```typescript
620
622
  // fallow-ignore-next-line unused-export
@@ -766,7 +768,7 @@ Manual files:
766
768
  "hooks": [
767
769
  {
768
770
  "type": "command",
769
- "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/fallow-gate.sh"
771
+ "command": "d=\"$(pwd)\"; until [ -f \"$d/.claude/hooks/fallow-gate.sh\" ] || [ -e \"$d/.git\" ] || [ \"$d\" = / ]; do d=\"$(dirname \"$d\")\"; done; if [ -f \"$d/.claude/hooks/fallow-gate.sh\" ]; then cd \"$d\" && exec ./.claude/hooks/fallow-gate.sh; fi; exec \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/fallow-gate.sh"
770
772
  }
771
773
  ]
772
774
  }
@@ -780,19 +782,45 @@ Manual files:
780
782
  Prefer `fallow hooks install --target agent` to install this file. The script is written and maintained by fallow itself; the canonical source is [`crates/cli/src/setup_hooks/fallow-gate.sh`](https://github.com/fallow-rs/fallow/blob/main/crates/cli/src/setup_hooks/fallow-gate.sh).
781
783
 
782
784
  Behavior you can rely on:
785
+ - The handler walks up from the session directory to the nearest directory that holds `.claude/hooks/fallow-gate.sh` and runs the audit there, so a session in a package directory audits the install root. The walk stops at the first `.git` entry. When it finds no script, the handler runs the script under `$CLAUDE_PROJECT_DIR` from the session directory.
786
+ - The hook process can start in a directory that is not the session directory, for example in the main checkout when the session works in a nested git worktree. The script reads the session directory from the `cwd` field of the hook input and finds the audit root from it with the same walk. When the walk finds no script, for example with the user-scope script in `$HOME`, the audit root is the directory of the hook process when it is in the git work tree of the session. Else it is the git top level of the session directory, or the session directory outside a git repository. The audit root is never `$HOME`. The script compares physical paths, so a symlink does not change the walk. When the hook input has no usable `cwd`, the audit runs in the directory of the hook process.
783
787
  - Runs only when the intercepted command is a `git commit` or `git push`, including invocations that pass git-level options before the subcommand (`git -c user.name=x commit`, `git --no-pager commit`, `git -C dir push`, `git --git-dir=/x push`); anything else exits 0. Set `FALLOW_GATE_DEBUG=1` to log skipped commands to stderr.
784
788
  - Resolves `fallow` from PATH first, then `npx --no-install fallow` as a fallback. Skips with a stderr notice if neither is available or if `jq` is missing.
785
- - Enforces a version floor via `FALLOW_GATE_MIN_VERSION` (default `2.85.0`). Binaries below the floor are blocked with an upgrade hint. Set the env var to the empty string to disable the check.
789
+ - Enforces a version floor via `FALLOW_GATE_MIN_VERSION`. The installed gate script holds the default floor (currently `2.85.0`). Fallow maintainers raise that default by hand; `fallow hooks install` does not set it to the installed version. Binaries below the floor are blocked with an upgrade hint. Set the env var to the empty string to disable the check.
786
790
  - Runs `fallow audit --format json --quiet --explain --gate-marker agent` and, on verdict=`fail`, writes the full JSON envelope to stderr preceded by `fallow-gate: blocked by fallow <version> at <binary>` so the responsible binary is always identifiable. The gate marker lets local Impact record blocked-then-cleared agent gate events when Impact is enabled.
787
791
  - On runtime error (`{"error": true, ...}`) or unexpected non-zero exit, fails open with a one-line stderr notice; warn verdicts pass through silently.
788
792
 
789
- Codex fallback (add to repo root `AGENTS.md`):
793
+ ### `.codex/hooks.json`
794
+
795
+ Codex reads the same PreToolUse shape and runs the same gate script from `.codex/hooks/fallow-gate.sh`. Codex runs hook commands from the session directory, so the handler walks up to the nearest directory that holds the gate script (the install root) and runs it there. The walk stops at the first `.git` entry, so a nested worktree does not reach the gate of the checkout around it:
796
+
797
+ ```json
798
+ {
799
+ "hooks": {
800
+ "PreToolUse": [
801
+ {
802
+ "matcher": "^Bash$",
803
+ "hooks": [
804
+ {
805
+ "type": "command",
806
+ "command": "d=\"$(pwd)\"; until [ -f \"$d/.codex/hooks/fallow-gate.sh\" ] || [ -e \"$d/.git\" ] || [ \"$d\" = / ]; do d=\"$(dirname \"$d\")\"; done; if [ -f \"$d/.codex/hooks/fallow-gate.sh\" ]; then cd \"$d\" && exec ./.codex/hooks/fallow-gate.sh; fi; exit 0"
807
+ }
808
+ ]
809
+ }
810
+ ]
811
+ }
812
+ }
813
+ ```
814
+
815
+ Codex loads project hooks only when the project `.codex/` layer is trusted, and it asks you to review each new or changed hook in `/hooks` before it runs.
816
+
817
+ Excerpt of the `AGENTS.md` routing block (written for Codex, also read by other agents; the full text is `AGENTS_BLOCK_BODY` in `crates/cli/src/setup_hooks.rs`):
790
818
 
791
819
  ```md
792
- Before any `git commit` or `git push`, run `fallow audit --format json --quiet --explain --gate-marker agent`. If the verdict is `fail`, fix the reported findings before retrying. Treat JSON runtime errors like `{ "error": true, ... }` as non-blocking.
820
+ Fallow checks the changed code before each `git commit` and `git push`. In Claude Code and Codex, a PreToolUse hook from `fallow agent install` runs this check and blocks the command when the verdict is `fail`. Other agents run the check themselves: `fallow audit --format json --quiet --explain --gate-marker agent`.
793
821
  ```
794
822
 
795
- Keep `fallow audit` in CI alongside this local gate. The hook only runs for Claude Code, not for human pushes or other agents, so it is a reinforcement layer rather than a replacement for server-side enforcement.
823
+ Keep `fallow audit` in CI alongside this local gate. The hooks run only for Claude Code and Codex, not for human pushes or other agents, so they are a reinforcement layer rather than a replacement for server-side enforcement.
796
824
 
797
825
  ### Remove the hook
798
826
 
@@ -800,10 +828,10 @@ Keep `fallow audit` in CI alongside this local gate. The hook only runs for Clau
800
828
  fallow hooks uninstall --target agent
801
829
  ```
802
830
 
803
- Removes the fallow-gate handler from `.claude/settings.json` (preserving any other handlers in the same matcher group), deletes `.claude/hooks/fallow-gate.sh` if it still carries the `# Generated by fallow setup-hooks.` marker, and strips the managed block from `AGENTS.md`. Idempotent: a second run reports `unchanged` / `not present` and exits 0.
831
+ Removes the fallow-gate handler from `.claude/settings.json` and `.codex/hooks.json` (preserving any other handlers in the same matcher group, and deleting `.codex/hooks.json` when nothing else is left in it), deletes each `fallow-gate.sh` that still carries the `# Generated by fallow setup-hooks.` marker, and strips the managed block from `AGENTS.md`. Idempotent: a second run reports `unchanged` / `not present` and exits 0.
804
832
 
805
833
  Use `--force` to remove a hook script that the user has edited (the marker is no longer present). Use `--dry-run` to preview without touching files.
806
834
 
807
835
  ### Distinguish from `fallow hooks install --target git`
808
836
 
809
- `fallow hooks install --target git` is a different target: it scaffolds a shell-level Git pre-commit hook under `.git/hooks/` that runs `fallow` on changed files. That is the *human* enforcement path. `fallow hooks install --target agent` is the *agent* enforcement path, targeting `.claude/` and `AGENTS.md`. Both can live in the same repo: git hooks catch human commits, the agent gate catches agent commits.
837
+ `fallow hooks install --target git` is a different target: it scaffolds a shell-level Git pre-commit hook under `.git/hooks/` that runs `fallow` on changed files. That is the *human* enforcement path. `fallow hooks install --target agent` is the *agent* enforcement path, targeting `.claude/`, `.codex/`, and `AGENTS.md`. Both can live in the same repo: git hooks catch human commits, the agent gate catches agent commits.
@@ -0,0 +1,50 @@
1
+ ---
2
+ name: fallow-setup
3
+ description: Set up or modernize code-quality tooling for JavaScript and TypeScript projects. Use when creating a project, adding code-health or CI quality checks, making a repository agent-ready, or consolidating dead-code, duplication, architecture, dependency, and changed-code analysis. Do not use for formatting-only, lint-rule-only, or TypeScript type-error tasks. Do not use to run an analysis of a project that is already set up; use the fallow skill for that.
4
+ license: MIT
5
+ ---
6
+
7
+ # Fallow setup: code-quality tooling for JavaScript and TypeScript
8
+
9
+ This skill sets up the code-quality toolchain of a JavaScript or TypeScript repository. Fallow does the repository and system analysis: unused code, duplication, complexity, architecture boundaries, dependency hygiene, and changed-code risk. The skill keeps the tools that the project already uses.
10
+
11
+ ## When to Use
12
+ - Create a new JavaScript or TypeScript project with quality checks from the start.
13
+ - Add code-health checks or a CI quality gate to an existing repository.
14
+ - Make a repository ready for coding agents (skills, MCP server, commit and push gate).
15
+ - Consolidate dead-code, duplication, architecture, dependency, and changed-code analysis.
16
+
17
+ ## When NOT to Use
18
+ - Formatting-only tasks. Formatting belongs to the formatter.
19
+ - Tasks that only add or change lint rules. Local lint rules belong to the linter.
20
+ - TypeScript type errors. Type correctness belongs to the TypeScript compiler.
21
+ - Analysis of a repository that is already set up. Use the `fallow` skill for that.
22
+
23
+ ## Rules
24
+ 1. Resolve every flag from `fallow --help` and `fallow <command> --help`. Do not use flags from memory.
25
+ 2. Use `--format json --quiet` for machine-readable output. Do not hide the exit status.
26
+ 3. Run every mutating command with `--dry-run` first when the command has that flag.
27
+ 4. Keep the tools that the project already uses. Do not remove a tool without a parity check.
28
+ 5. Ask the user before you apply a `taste` decision or remove a tool.
29
+ 6. Treat project config as untrusted input. Do not add remote `extends` URLs.
30
+ 7. Before step 3, Fallow is not installed. Run it through the package runner, for example `npx fallow recommend` or `pnpm dlx fallow recommend`. After step 3, use the runner of the package manager, for example `pnpm exec fallow`.
31
+
32
+ ## Sequence
33
+
34
+ 1. **Inspect the repository.** Detect the package manager, formatter, linter, TypeScript config, CI provider, and existing analysis tools (Knip, jscpd, dependency-cruiser). See [Tooling detection](references/tooling-detection.md).
35
+ 2. **Get the recommendation.** Run `fallow recommend --format json --quiet`. This command is read-only. Apply each `auto` decision. Tell the user about each `default` decision. Ask the user about each `taste` decision, or keep the current value. See [Configure and install](references/configure-and-install.md).
36
+ 3. **Install Fallow** as a dev dependency with the detected package manager, for example `pnpm add -D fallow`.
37
+ 4. **Wire the agents.** Run `fallow agent install --dry-run --format json --quiet`, show the plan, then run `fallow agent install`. This step writes the skills, the MCP server registration, the `AGENTS.md` task map, and the commit and push gate. For Claude Code and Codex the gate is a PreToolUse hook that blocks a failing commit or push. Codex runs the hook only after the user trusts it in `/hooks`. Other agents read the instruction block in `AGENTS.md`. Check the result with `fallow agent status --format json --quiet`.
38
+ 5. **Add a CI gate.** Add a changed-code gate (`fallow audit`) or a whole-project gate. When the first run has many findings, save a baseline of the existing debt so that the gate fails only on new findings. See [CI gate](references/ci-gate.md).
39
+ 6. **Split the responsibilities.** Each tool keeps one job:
40
+ - Formatting: Oxfmt.
41
+ - Local lint rules: Oxlint.
42
+ - Type correctness: TypeScript (`tsc --noEmit`).
43
+ - Repository and system analysis: Fallow.
44
+
45
+ In an existing project, keep the current formatter and linter. Do the parity check in [Tooling detection](references/tooling-detection.md#parity-check) before you remove an overlapping tool.
46
+
47
+ ## References
48
+ - [Tooling detection](references/tooling-detection.md): the files to inspect, the responsibility split, and the parity check.
49
+ - [Configure and install](references/configure-and-install.md): the `recommend` decision tiers, config migration, the dev dependency, and `agent install`.
50
+ - [CI gate](references/ci-gate.md): the GitHub Action, the GitLab template, the CLI gate, and baselines.
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "Fallow Setup"
3
+ short_description: "Set up JS and TS code-quality tooling"
4
+ default_prompt: "Use $fallow-setup to set up code-quality tooling, agent wiring, and a CI quality gate for this repository."
@@ -0,0 +1,61 @@
1
+ # CI gate
2
+
3
+ Add one gate to the CI provider that the project already uses. Exit code 0 means that the gate passed. Exit code 1 means that the gate found error-severity findings or that the audit result is `fail`. Exit code 2 means invalid input or an execution error. Do not force a successful exit status, because that hides the gate result.
4
+
5
+ ## Select the gate
6
+
7
+ | Gate | Command | Fails on | Baseline |
8
+ |---|---|---|---|
9
+ | Changed-code gate | `fallow audit` | Findings that the change introduces in the changed files | Not necessary |
10
+ | Whole-project gate | `fallow` | Every error-severity finding in the project | Necessary when the first run has many findings |
11
+
12
+ Start with the changed-code gate. `fallow audit` uses `--gate new-only` by default, so findings that existed before the change do not fail it. The audit needs the git history of the base ref. In CI, fetch the full history.
13
+
14
+ ## GitHub Actions
15
+
16
+ ```yaml
17
+ - uses: actions/checkout@v4
18
+ with:
19
+ fetch-depth: 0
20
+ - uses: fallow-rs/fallow@v3
21
+ with:
22
+ command: audit
23
+ ```
24
+
25
+ The Action installs the Fallow version that `package.json` names. To start in report-only mode, add `fail-on-issues: false`. Remove it when the team accepts the gate.
26
+
27
+ ## GitLab CI
28
+
29
+ ```yaml
30
+ include:
31
+ - remote: 'https://raw.githubusercontent.com/fallow-rs/fallow/v<version>/ci/gitlab-ci.yml'
32
+
33
+ fallow:
34
+ extends: .fallow
35
+ variables:
36
+ FALLOW_COMMAND: "audit"
37
+ ```
38
+
39
+ Replace `<version>` with the installed Fallow version. When the runners cannot reach `raw.githubusercontent.com`, run `fallow ci-template gitlab --vendor` and include the vendored files locally.
40
+
41
+ ## Other CI providers
42
+
43
+ Run the CLI directly after the dependencies are installed:
44
+
45
+ ```bash
46
+ npx fallow audit --base origin/main --format json --quiet
47
+ ```
48
+
49
+ Use the runner of the detected package manager, for example `pnpm exec fallow`.
50
+
51
+ ## Baselines
52
+
53
+ A whole-project gate on an existing project often reports a large backlog on the first run. Save a baseline of that backlog, so that the gate fails only on new findings:
54
+
55
+ ```bash
56
+ fallow dead-code --format json --quiet --save-baseline fallow-baselines/dead-code.json
57
+ fallow health --format json --quiet --save-baseline fallow-baselines/health.json
58
+ fallow dupes --format json --quiet --save-baseline fallow-baselines/dupes.json
59
+ ```
60
+
61
+ Commit the baseline files. Pass each file to the matching command with `--baseline <path>`. `fallow audit` uses `--dead-code-baseline`, `--health-baseline`, and `--dupes-baseline` instead. `--fail-on-baseline-growth` makes a committed baseline shrink-only. After a fix, `fallow baselines prune` removes the entries that match no current finding, so `--fail-on-stale-baseline` stays green. Point `audit.deadCodeBaseline`, `audit.healthBaseline` and `audit.dupesBaseline` at the files so that one command prunes all three. Tell the user how many findings the baseline contains, and suggest that the team removes them by category.
@@ -0,0 +1,56 @@
1
+ # Configure and install
2
+
3
+ ## Recommendation
4
+
5
+ `fallow recommend --format json --quiet` inspects the project and changes nothing. It always exits 0. The envelope has `kind: "recommendation"` and these fields:
6
+
7
+ | Field | Content |
8
+ |---|---|
9
+ | `detected` | The project facts: monorepo layout, TypeScript, test framework, UI framework, Storybook, package manager |
10
+ | `proposed_config` | A safe starting config |
11
+ | `decisions[]` | One entry per setting: `setting`, `value`, `rationale`, `kind`, `question` |
12
+ | `config_schema_command` | The command that prints the config JSON Schema |
13
+
14
+ Use `kind` to decide what to do with each decision:
15
+
16
+ | `kind` | Meaning | Action |
17
+ |---|---|---|
18
+ | `auto` | Fallow decided the value from the detection | Apply it. |
19
+ | `default` | A disclosed default that the user can change | Apply it and tell the user the `rationale` in one line. |
20
+ | `taste` | A subjective choice | Keep the current value, or ask the user. `question` has a `header`, a `question`, and `options[]` with a `label` and a `description`. Show these options as they are. |
21
+
22
+ When the project has no Fallow config, write `proposed_config` plus the answers to `.fallowrc.json`. When the project has a config, change only the settings that the user approved. Validate the keys with `fallow config-schema`. Check the loaded config with `fallow config --format json --quiet`.
23
+
24
+ When the project has a Knip, jscpd, or stylelint config, preview the migration with `fallow migrate --dry-run`. Merge the result with the recommendation. Do not delete the old config in this step. The [parity check](tooling-detection.md#parity-check) decides when it can go.
25
+
26
+ ## Dev dependency
27
+
28
+ Install Fallow as a dev dependency with the detected package manager:
29
+
30
+ | Package manager | Command |
31
+ |---|---|
32
+ | npm | `npm install --save-dev fallow` |
33
+ | pnpm | `pnpm add -D fallow` |
34
+ | Yarn | `yarn add -D fallow` |
35
+ | Bun | `bun add -d fallow` |
36
+
37
+ A dev dependency pins one Fallow version for every developer, every agent, and CI. Add scripts to `package.json` when the project uses scripts for its other checks, for example `"fallow": "fallow"` and `"fallow:audit": "fallow audit"`.
38
+
39
+ ## Agent wiring
40
+
41
+ `fallow agent install` wires Fallow into Claude Code, Codex, and Cursor in one pass. It detects the harnesses from the project, the home directory, and the session environment. `--harness` selects them explicitly.
42
+
43
+ 1. Run `fallow agent install --dry-run --format json --quiet`. Each entry in `steps[]` has a `step`, a `status`, and a `path`. Show the plan to the user.
44
+ 2. Run `fallow agent install`. Add `--without <step>` to skip a step. The steps are `guide`, `skill`, `mcp`, and `hooks`.
45
+ 3. Run `fallow agent status --format json --quiet`. Check only the entries of the harnesses that step 1 selected. Status also lists the other harnesses (for example `.cursor/mcp.json` in a Claude-only project) as `absent`, which is correct. Act on `next_actions[]` for a `stale` entry of a selected harness.
46
+
47
+ The steps write these items:
48
+
49
+ | Step | Result |
50
+ |---|---|
51
+ | `guide` | The task map in `AGENTS.md`, and an `@AGENTS.md` import in `CLAUDE.md` for Claude Code |
52
+ | `skill` | The Fallow skills under `.claude/skills/` and `.agents/skills/` |
53
+ | `mcp` | The MCP server registration for each harness |
54
+ | `hooks` | The commit and push gate: a PreToolUse hook for Claude Code (`.claude/settings.json`) and for Codex (`.codex/hooks.json`), plus a routing block in `AGENTS.md` |
55
+
56
+ Exit code 2 means that a step is `refused` or `failed`. Read the `reason` of that step. `skill_name_taken` means that a skill with the same name exists and Fallow did not write it. Do not pass `--force` without the approval of the user.