fallow 3.30.0 → 3.32.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.
- package/README.md +18 -13
- package/capabilities.json +402 -177
- package/issue-registry.json +241 -166
- package/package.json +14 -13
- package/schema.json +89 -6
- package/skills/fallow/SKILL.md +30 -9
- package/skills/fallow/references/cli-reference.md +103 -44
- package/skills/fallow/references/gotchas.md +45 -13
- package/skills/fallow/references/issue-types.md +4 -2
- package/skills/fallow/references/mcp.md +48 -6
- package/skills/fallow/references/node-bindings.md +9 -3
- package/skills/fallow/references/patterns.md +37 -12
- package/skills/fallow-setup/SKILL.md +50 -0
- package/skills/fallow-setup/agents/openai.yaml +4 -0
- package/skills/fallow-setup/references/ci-gate.md +61 -0
- package/skills/fallow-setup/references/configure-and-install.md +56 -0
- package/skills/fallow-setup/references/tooling-detection.md +44 -0
- package/types/output-contract.d.ts +1044 -35
|
@@ -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.
|
|
58
|
+
The `--quiet` flag suppresses progress bars on stderr. Keep stderr separate from stdout when parsing JSON.
|
|
59
59
|
|
|
60
60
|
---
|
|
61
61
|
|
|
62
|
-
|
|
62
|
+
<a id="--changed-since-shows-only-new-issues"></a>
|
|
63
63
|
|
|
64
|
-
|
|
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
|
-
#
|
|
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
|
-
|
|
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
|
-
- **
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
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
|
|
@@ -215,7 +219,7 @@ Commit the baseline file to your repo. Update it periodically as you fix existin
|
|
|
215
219
|
|
|
216
220
|
## Duplication Modes Affect What's Detected
|
|
217
221
|
|
|
218
|
-
|
|
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
|
-
|
|
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
|
|
|
@@ -436,6 +444,30 @@ Fallow treats `Config` and `Result` in `./types.ts` as used. Works with `@param`
|
|
|
436
444
|
|
|
437
445
|
---
|
|
438
446
|
|
|
447
|
+
## Command File Arguments Are Entry Points
|
|
448
|
+
|
|
449
|
+
A file that a command names in a `package.json` script, a CI file (GitHub Actions, GitLab CI), a Dockerfile, a Procfile, or `fly.toml` becomes an entry point: `node scripts/seed.ts` keeps `scripts/seed.ts` and its imports reachable.
|
|
450
|
+
|
|
451
|
+
Formatters, linters, and checkers are the exception. They read their file arguments but do not run them, so `eslint src/a.ts`, `prettier --check "**/*.ts"`, `oxlint src/`, `biome check`, `stylelint`, `textlint`, and similar tools make no entry points. This applies to the common package-manager and wrapper forms (`npx`, `pnpm exec`, `pnpm --filter web exec`, `pnpm -r exec`, `yarn run`, `cross-env`, `dotenv -e .env --`, `varlock run --`), and to a call of a script that runs the tool (`npm run lint -- src/a.ts`, `npm run lint src/a.ts`, `yarn lint src/a.ts`). The tool stays a used dependency, its `--config` file stays tracked, and a module that it loads through a flag (`eslint -f ./fmt.js`, `prettier --plugin=./plugin.mjs`) stays reachable.
|
|
452
|
+
|
|
453
|
+
A command in a workspace package that the command selects resolves its file arguments against the directory of that package. `yarn workspace web node scripts/a.ts`, `pnpm --filter web exec tsx scripts/a.ts`, `npm exec -w web -- tsx scripts/a.ts`, and a call of a script of that package (`npm run -w web gen -- scripts/a.ts`) make `scripts/a.ts` of the `web` package an entry point. A pnpm filter can be a name, a name glob (`'@acme/*'`), a directory (`./packages/*`, `{packages/web}`), or an exclusion (`'!web'`). A selection of several packages resolves the file in each package where the file exists. This includes every package: `pnpm -r exec tsx scripts/a.ts`, `yarn workspaces foreach -A exec tsx scripts/a.ts` (narrowed by `--include` and `--exclude`), `yarn workspaces run gen scripts/a.ts`, and `npm --workspaces run gen -- scripts/a.ts`. `yarn workspaces foreach -A` also runs in the root package, as yarn berry does, and its `--include` and `--exclude` match a workspace name or directory (`.` is the root). `pnpm -w` also selects the root package. `--include-workspace-root` adds the root package: in pnpm to `-r` and to a filter that only excludes packages (`--filter '!web'`), and in npm to every workspace selection (`-w web`, `--workspaces`). Without it, `pnpm -r`, `yarn workspaces run`, and `npm --workspaces` leave out the root package. From a workspace package, `npm --workspaces` selects only that package. A `start` script that calls a script in selected packages (`pnpm -r run serve`, `pnpm -C packages/web run serve`) makes that script a runtime script of each package. A script call in the directory of a workspace package (`pnpm -C packages/web run gen scripts/a.ts`, `npm --prefix packages/web run gen -- scripts/a.ts`, `yarn --cwd packages/web gen scripts/a.ts`) runs the script of that package with the forwarded arguments. The formatter and linter rule above still applies in each selected package.
|
|
454
|
+
|
|
455
|
+
A command that runs in workspace packages that Fallow cannot resolve makes no entry points, because those packages resolve the paths against their own directories. This covers the pnpm dependency and changed-package filters (`web...`, `[origin/main]`), the other `yarn workspaces foreach` selections (`--since`, `--recursive`, `--from`, `--worktree`, `--no-private`), and task runners (`turbo run lint -- src/a.ts`, `nx`, `lerna`). The binary stays a used dependency. A command in another directory (`pnpm -C docs exec tsx scripts/a.ts`, `npm --prefix`, `yarn --cwd`) resolves its file arguments against that directory. `yarn node <file>` runs the file with Node.js, also after `yarn --cwd <dir>` and `yarn workspace <name>`.
|
|
456
|
+
|
|
457
|
+
A declared script with the name of a tool runs instead of the tool. With `"eslint": "node tools/check.js"`, `yarn eslint src/a.ts` keeps `src/a.ts` as an entry point.
|
|
458
|
+
|
|
459
|
+
For another command whose file arguments are data, list it in `ignoreCommandEntries`:
|
|
460
|
+
|
|
461
|
+
```jsonc
|
|
462
|
+
{
|
|
463
|
+
"ignoreCommandEntries": ["my-codegen"]
|
|
464
|
+
}
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
`["*"]` turns off entry points from all commands, including modules that a linter loads through a flag (`eslint -f ./fmt.js`); declare the real entries in `entry` instead.
|
|
468
|
+
|
|
469
|
+
---
|
|
470
|
+
|
|
439
471
|
## JSX `<script src>` and `<link href>` Are Asset References
|
|
440
472
|
|
|
441
473
|
Inside JSX/TSX files, lowercase intrinsic `<script src="...">` and `<link rel="stylesheet|modulepreload" href="...">` are tracked as asset references, same as in plain HTML files. This is needed for SSR frameworks like Hono where layout components emit HTML via JSX.
|
|
@@ -630,14 +662,14 @@ Both require a `GITLAB_TOKEN` CI/CD variable (project access token with `api` sc
|
|
|
630
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:
|
|
631
663
|
|
|
632
664
|
```
|
|
633
|
-
fallow license refresh: your stored license is too stale to refresh
|
|
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)
|
|
634
666
|
```
|
|
635
667
|
|
|
636
668
|
Stable codes the CLI surfaces today:
|
|
637
669
|
|
|
638
670
|
| Code | Operation | Meaning |
|
|
639
671
|
|------|-----------|---------|
|
|
640
|
-
| `token_stale` | `refresh` | Stored JWT is more than 45 days past its `exp`.
|
|
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. |
|
|
641
673
|
| `invalid_token` | `refresh` | Stored JWT is missing required claims (e.g. `sub`). Reactivate. |
|
|
642
674
|
| `unauthorized` | `refresh` or `trial` | Auth failed. Reactivate. |
|
|
643
675
|
| `rate_limit_exceeded` | `trial` | Trial endpoint is capped at 5 per hour per IP. Wait or use a different network. |
|
|
@@ -30,10 +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
|
-
| `
|
|
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
|
+
| `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 |
|
|
34
35
|
| `boundary-coverage` | `--boundary-violations` | - | `// fallow-ignore-file boundary-violation` | Source file matches no configured architecture boundary zone; Requires boundaries.coverage.requireAllFiles |
|
|
35
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 |
|
|
36
|
-
| `policy-violation` | `--policy-violations` | - | `// fallow-ignore-next-line policy-violation` | Calls, imports,
|
|
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. |
|
|
37
38
|
| `stale-suppression` | `--stale-suppressions` | - | - | `fallow-ignore` comments or `@expected-unused` JSDoc tags that no longer match any issue |
|
|
38
39
|
| `missing-suppression-reason` | `--stale-suppressions` | - | - | Suppression comment omits a required reason |
|
|
39
40
|
| `unused-catalog-entry` | `--unused-catalog-entries` | yes | - | `pnpm-workspace.yaml` entries no workspace package.json references via `catalog:` (default `warn`) |
|
|
@@ -46,6 +47,7 @@ The `generated:issue-types` table below is regenerated from `fallow schema` by s
|
|
|
46
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 |
|
|
47
48
|
| `unprovided-inject` | `--unprovided-injects` | - | `// fallow-ignore-next-line unprovided-inject` | inject() / getContext() reads a key that no provide() / setContext() supplies |
|
|
48
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. |
|
|
49
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 |
|
|
50
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 |
|
|
51
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 |
|
|
@@ -12,9 +12,9 @@ When using fallow via MCP (`fallow-mcp`), the following tools are available:
|
|
|
12
12
|
| Tool | Kind | License | CLI fallback | Key params | Description |
|
|
13
13
|
|---|---|---|---|---|---|
|
|
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
|
-
| `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) + 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 |
|
|
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://
|
|
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://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
18
|
| `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
19
|
| `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
20
|
| `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. |
|
|
@@ -27,6 +27,8 @@ When using fallow via MCP (`fallow-mcp`), the following tools are available:
|
|
|
27
27
|
| `get_importance` | runtime-coverage | freemium | `fallow health --runtime-coverage <path> --format json --quiet` | `coverage`, `group_by` | Runtime-context slice for production-importance review. Same params as `check_runtime_coverage`; read `runtime_coverage.importance` for stable `fallow:importance:<hash>` IDs, invocations, cyclomatic complexity, owner count, 0-100 score, and templated reason. |
|
|
28
28
|
| `get_cleanup_candidates` | runtime-coverage | freemium | `fallow health --runtime-coverage <path> --format json --quiet` | `coverage`, `group_by` | Runtime-context slice for cleanup review. Same params as `check_runtime_coverage`; read `runtime_coverage.findings` for `safe_to_delete`, `review_required`, `low_traffic`, and `coverage_unavailable`. |
|
|
29
29
|
| `get_cloud_runtime_context` | runtime-coverage | freemium | `fallow coverage analyze --cloud --repo <owner/repo> --format json --quiet` | `repo`, `period_days`, `environment`, `commit_sha`, `top` | Cloud-backed runtime-context slice, and the only MCP tool that makes a network call. Required `repo` (`owner/repo`); `project_id`, `period_days` (1-90, default 30), `environment`, and `commit_sha` narrow the cloud selection, while `production`, `top`, and `min_invocations_hot` behave as on `check_runtime_coverage`. The key is `FALLOW_API_KEY` in the server environment and never a param: without it the call is refused with `code: "cloud_api_key_missing"` before anything runs. Returns the same `runtime_coverage` block as the local tools, joined against the checkout at `root`, so a `root` on a different revision quietly empties `findings` and raises a `cloud_functions_unmatched` warning. Confirm `runtime_coverage.summary.data_source` is `cloud`, and read the source-map confidence table below before acting on file-level signals. |
|
|
30
|
+
| `get_cloud_review_packet` | runtime-coverage | freemium | `fallow coverage review-packet --repo <owner/repo> --file <path> --format json --quiet` | `repo`, `files`, `functions`, `period_days`, `project_id` | Production facts of a few changed files or functions, read from fallow cloud without the full runtime-context pull |
|
|
31
|
+
| `get_cloud_deployment_changes` | runtime-coverage | freemium | `fallow coverage deployment-changes --repo <owner/repo> --sha <sha> --format json --quiet` | `repo`, `sha`, `base`, `change` | How production behavior changed between two deployments, read from the fallow cloud change report |
|
|
30
32
|
| `get_token_blast_radius` | analysis | free | `fallow health --css --format json --quiet` | - | Design-token blast radius for Tailwind v4 @theme tokens and CSS-in-JS token definitions (StyleX, vanilla-extract, PandaCSS): per token, a consumer_count (static lower bound) and a capped located consumers[] sample tagged theme-var/css-var/utility/apply (Tailwind), js-member (member access), or js-call (StyleX theme-group and Panda token calls); descriptive context for sizing a token change, never a deletion gate |
|
|
31
33
|
| `audit` | analysis | free | `fallow audit --format json --quiet` | `gate`, `base`, `css_deep`, `max_crap`, `coverage`, `runtime_coverage` | Combined dead-code + complexity + duplication + styling for changed files, returns verdict. Styling analytics are enabled by default; CSS and CSS-in-JS evidence can add `styling_findings`, `css_analytics`, and `styling_health` under the health sub-result. Set `gate` to `"new-only"` or `"all"`. Set `css_deep: false` to skip project-wide styling reachability while keeping local styling checks, or `css_deep: true` to force it back on when config disables it. Optional `runtime_coverage` (V8 dir / V8 JSON / Istanbul JSON) folds runtime findings into the same call; `min_invocations_hot` tunes the hot-path threshold (default 100). Runtime evidence appears under the audit `complexity` sub-result, including `coverage_intelligence` when combined evidence yields actionable recommendations. |
|
|
32
34
|
| `decision_surface` | analysis | free | `fallow decision-surface --format json --quiet` | `base`, `max_decisions`, `workspace` | Surface the few consequential structural decisions a change embeds (coupling, public API, dependency), each as a judgment question with the routed expert; ranked, capped, and signal_id-anchored |
|
|
@@ -48,14 +50,20 @@ When using fallow via MCP (`fallow-mcp`), the following tools are available:
|
|
|
48
50
|
| `trace_error` | trace | free | `fallow trace-error - --format json --quiet` | `trace` | Resolve a runtime stack trace's frames against the project graph |
|
|
49
51
|
| `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 |
|
|
50
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` |
|
|
51
|
-
| `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_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 |
|
|
52
54
|
<!-- generated:mcp-tools:end -->
|
|
53
55
|
|
|
54
56
|
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`.
|
|
55
57
|
|
|
56
58
|
## Resource catalogue
|
|
57
59
|
|
|
58
|
-
Resources
|
|
60
|
+
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`).
|
|
61
|
+
|
|
62
|
+
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.
|
|
63
|
+
|
|
64
|
+
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`.
|
|
65
|
+
|
|
66
|
+
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`).
|
|
59
67
|
|
|
60
68
|
<!-- generated:mcp-resources:start -->
|
|
61
69
|
| Resource | Name | Kind | MIME | Description |
|
|
@@ -95,6 +103,34 @@ ambiguity or reachable references.
|
|
|
95
103
|
|
|
96
104
|
`trace_export` never carries a `semantic` block: it is API-backed in-process and answers from the graph alone.
|
|
97
105
|
|
|
106
|
+
## Scoped cloud reads
|
|
107
|
+
|
|
108
|
+
Use these reads when the project sends production coverage to Fallow Cloud. The CLI reads the key from `--api-key` or `FALLOW_API_KEY`; the MCP tools read `FALLOW_API_KEY` from the server environment only. Prefer the two scoped reads: they return only the functions or the deploy you ask about. `get_cloud_runtime_context` downloads the evidence of every function and runs a full local analysis first, so keep it for whole-project questions.
|
|
109
|
+
|
|
110
|
+
| Question | CLI | MCP tool |
|
|
111
|
+
|:---------|:----|:---------|
|
|
112
|
+
| Is a changed function hot, cold, or tested? | `fallow coverage review-packet --repo <owner/repo>` | `get_cloud_review_packet` |
|
|
113
|
+
| Did the last deploy change what runs? | `fallow coverage deployment-changes --repo <owner/repo>` | `get_cloud_deployment_changes` |
|
|
114
|
+
|
|
115
|
+
**Before an edit.** Without `--file` or `--function`, `review-packet` sends the source files changed against the base (`--base`, then `FALLOW_AUDIT_BASE`, then the merge-base). Narrow it with `--file <path>` or `--function <file>:<name>[:<line>]`, both repeatable; over MCP pass `files[]` or `functions[{file, name, line?}]`. Per function, read:
|
|
116
|
+
|
|
117
|
+
- `hit_count`: the production call count. Rank hot functions by it, not by `prod_hit_count`, which counts tagged traffic only.
|
|
118
|
+
- `tracking_state`: `called`, `never_called`, or `untracked` in the current deployment.
|
|
119
|
+
- `covered_by_test`: `true` or `null`. `null` means no test evidence, not "no test". A hot function with `covered_by_test: null` is a high-risk edit.
|
|
120
|
+
- `blast_radius.caller_count` and `blast_radius.caller_sites`: callers when the data exists; `null` is unknown, not zero.
|
|
121
|
+
|
|
122
|
+
An entry in `not_found` has no cloud data. Absence is not evidence that the code is cold.
|
|
123
|
+
|
|
124
|
+
**Before a delete.** Require both static and runtime evidence:
|
|
125
|
+
|
|
126
|
+
1. `fallow dead-code --trace <file>:<export>` (MCP `trace_export`) confirms the static side.
|
|
127
|
+
2. `period_tracking_state` is `never_called`. It covers the whole period, while `tracking_state` covers only the current deployment. A function with `period_tracking_state: "called"` never gets a `safe_to_delete` verdict.
|
|
128
|
+
3. `evidence_window.observed_hours` is large enough for the traffic of that code. An admin page, a yearly job, an error handler, or a flagged feature can stay unvisited for a long time: "never called" there means "not visited", not "dead". Ask the owner or keep the code.
|
|
129
|
+
|
|
130
|
+
**After a deploy.** `deployment-changes` compares `--sha` (default: git HEAD) with `--base` (default: the previous deployment with production runtime). Each function gets one kind: `stopped`, `new_not_called`, `heated_up`, `cooled_down`, `new_called`, or `unchanged`. Filter with `--change <kind>`, page with `--limit` (1 to 200) and `--cursor`. When `comparable` is `false`, report `reason` and claim no stop and no rate change: `head_warming_up`, `head_short_window`, and `head_insufficient_runtime` mean "try again later"; `no_base_deployment` means there is nothing to compare; `runtime_surfaces_differ`, `runtime_surfaces_unknown`, and `function_set_differs` mean the two deployments do not measure the same code. The report is context and never proves that a function is dead.
|
|
131
|
+
|
|
132
|
+
**Paths.** Open and edit files by `repo_path`. `file_path` is the path the runtime reported (for example `/app/src/x.ts` in a container) and often does not exist in the checkout.
|
|
133
|
+
|
|
98
134
|
## Runtime source-map confidence for cloud runtime tools
|
|
99
135
|
|
|
100
136
|
| Values | Meaning | Agent action |
|
|
@@ -108,8 +144,14 @@ ambiguity or reachable references.
|
|
|
108
144
|
|
|
109
145
|
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.
|
|
110
146
|
|
|
111
|
-
|
|
147
|
+
Every dead-code, health, and duplication finding in JSON responses includes a structured `actions` array for programmatic fixes or suppression.
|
|
112
148
|
|
|
113
149
|
`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.
|
|
114
150
|
|
|
115
|
-
`dead-code`, `health`, `dupes`, bare `fallow`, and `audit` JSON output
|
|
151
|
+
`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.
|
|
152
|
+
|
|
153
|
+
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`.
|
|
154
|
+
|
|
155
|
+
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.
|
|
156
|
+
|
|
157
|
+
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
|
-
|
|
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
|
-
|
|
18
|
+
The async functions are `detectDeadCode`, `detectCircularDependencies`, `detectBoundaryViolations`, `detectDuplication`, `detectSimilarCode`, `detectFeatureFlags`, `computeComplexity`, and `computeHealth`. Each returns the same JSON envelope the CLI emits for `--format json`.
|
|
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://
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
|
@@ -446,7 +446,7 @@ Creates `.fallowrc.json` with mapped settings:
|
|
|
446
446
|
hidden, but matching files remain in the module graph; leading `!` exceptions
|
|
447
447
|
are preserved; multi-source findings stay visible unless every source owner
|
|
448
448
|
matches)
|
|
449
|
-
- knip `ignoreDependencies` → fallow `ignoreDependencies`
|
|
449
|
+
- 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
450
|
- 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
451
|
- Unmappable fields generate warnings with suggestions
|
|
452
452
|
|
|
@@ -614,7 +614,7 @@ export const dynamicallyUsed = createHandler();
|
|
|
614
614
|
|
|
615
615
|
### If the trace shows it's NOT used
|
|
616
616
|
|
|
617
|
-
The
|
|
617
|
+
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
618
|
|
|
619
619
|
```typescript
|
|
620
620
|
// fallow-ignore-next-line unused-export
|
|
@@ -766,7 +766,7 @@ Manual files:
|
|
|
766
766
|
"hooks": [
|
|
767
767
|
{
|
|
768
768
|
"type": "command",
|
|
769
|
-
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/fallow-gate.sh"
|
|
769
|
+
"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
770
|
}
|
|
771
771
|
]
|
|
772
772
|
}
|
|
@@ -780,19 +780,44 @@ Manual files:
|
|
|
780
780
|
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
781
|
|
|
782
782
|
Behavior you can rely on:
|
|
783
|
+
- 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.
|
|
783
784
|
- 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
785
|
- 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
|
|
786
|
+
- 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
787
|
- 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
788
|
- On runtime error (`{"error": true, ...}`) or unexpected non-zero exit, fails open with a one-line stderr notice; warn verdicts pass through silently.
|
|
788
789
|
|
|
789
|
-
|
|
790
|
+
### `.codex/hooks.json`
|
|
791
|
+
|
|
792
|
+
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:
|
|
793
|
+
|
|
794
|
+
```json
|
|
795
|
+
{
|
|
796
|
+
"hooks": {
|
|
797
|
+
"PreToolUse": [
|
|
798
|
+
{
|
|
799
|
+
"matcher": "^Bash$",
|
|
800
|
+
"hooks": [
|
|
801
|
+
{
|
|
802
|
+
"type": "command",
|
|
803
|
+
"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"
|
|
804
|
+
}
|
|
805
|
+
]
|
|
806
|
+
}
|
|
807
|
+
]
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
```
|
|
811
|
+
|
|
812
|
+
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.
|
|
813
|
+
|
|
814
|
+
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
815
|
|
|
791
816
|
```md
|
|
792
|
-
|
|
817
|
+
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
818
|
```
|
|
794
819
|
|
|
795
|
-
Keep `fallow audit` in CI alongside this local gate. The
|
|
820
|
+
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
821
|
|
|
797
822
|
### Remove the hook
|
|
798
823
|
|
|
@@ -800,10 +825,10 @@ Keep `fallow audit` in CI alongside this local gate. The hook only runs for Clau
|
|
|
800
825
|
fallow hooks uninstall --target agent
|
|
801
826
|
```
|
|
802
827
|
|
|
803
|
-
Removes the fallow-gate handler from `.claude/settings.json` (preserving any other handlers in the same matcher group
|
|
828
|
+
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
829
|
|
|
805
830
|
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
831
|
|
|
807
832
|
### Distinguish from `fallow hooks install --target git`
|
|
808
833
|
|
|
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
|
|
834
|
+
`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,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. Tell the user how many findings the baseline contains, and suggest that the team removes them by category.
|