fallow 3.5.0 → 3.6.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 +40 -81
- package/capabilities.json +17 -2
- package/package.json +10 -10
- package/scripts/run-binary.test.js +71 -0
- package/skills/fallow/SKILL.md +8 -5
- package/skills/fallow/references/cli-reference.md +15 -13
- package/types/output-contract.d.ts +7 -9
package/README.md
CHANGED
|
@@ -2,98 +2,60 @@
|
|
|
2
2
|
|
|
3
3
|
**Codebase intelligence for TypeScript and JavaScript.**
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
One binary finds unused code, circular dependencies, duplication, complexity hotspots, boundary violations, and design-system styling drift. An optional paid layer, Fallow Runtime, adds production execution evidence. No AI inside the analyzer, and no TypeScript compiler or Node.js runtime needed for static analysis: runs are deterministic, with typed output contracts and traceable explanations.
|
|
6
6
|
|
|
7
7
|
[](https://github.com/fallow-rs/fallow/actions/workflows/ci.yml)
|
|
8
8
|
[](https://www.npmjs.com/package/fallow)
|
|
9
9
|
[](https://github.com/fallow-rs/fallow/blob/main/LICENSE)
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
It helps you answer: what changed, what got riskier, what should be reviewed, what should be refactored, and what can be safely removed. No AI inside the analyzer. Fallow produces deterministic findings, typed output contracts, and traceable explanations that downstream tools can trust.
|
|
14
|
-
|
|
15
|
-
Static analysis is open source. An optional runtime layer adds production execution evidence. Rust-native, sub-second, 123 framework plugins, no Node.js runtime dependency for analysis. fallow's edge is doing more in one pass: dead code, duplication, dependencies, complexity, architecture, styling consistency, and security candidates in a single tool. For head-to-head timings against [knip](https://knip.dev) and [jscpd](https://github.com/kucherenko/jscpd), see the [benchmarks](https://github.com/fallow-rs/fallow/tree/main/benchmarks): fallow is faster than knip on smaller projects, knip is faster on several larger repos, and jscpd's Rust rewrite is faster for raw duplication scanning.
|
|
16
|
-
|
|
17
|
-
## Installation
|
|
11
|
+
## Install
|
|
18
12
|
|
|
19
13
|
```bash
|
|
20
|
-
npm install --save-dev fallow # or: pnpm add -D fallow / yarn add -D fallow
|
|
14
|
+
npm install --save-dev fallow # or: pnpm add -D fallow / yarn add -D fallow
|
|
21
15
|
```
|
|
22
16
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
The package also ships a version-matched Agent Skill under `skills/fallow`.
|
|
26
|
-
For tools that need CLI and issue-surface metadata without spawning the binary,
|
|
27
|
-
`fallow/capabilities.json` mirrors `fallow schema` for the installed version.
|
|
28
|
-
TanStack Intent discovers it from `node_modules` automatically:
|
|
29
|
-
|
|
30
|
-
```bash
|
|
31
|
-
npx @tanstack/intent list
|
|
32
|
-
npx @tanstack/intent load fallow#fallow
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
For one-off CLI use without project-local skill discovery, run `npx fallow`.
|
|
36
|
-
|
|
37
|
-
Parsing fallow's JSON output in TypeScript? Import the typed shapes:
|
|
38
|
-
|
|
39
|
-
```ts
|
|
40
|
-
import type { CheckOutput, FallowJsonOutput } from "fallow/types";
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
The types are generated from the same schema as the VS Code extension and pin to the CLI version you install. See [docs.fallow.tools](https://docs.fallow.tools) for the full output contract.
|
|
17
|
+
This installs the `fallow` CLI plus the `fallow-lsp` and `fallow-mcp` launchers, so editor and agent integrations resolve the project-local binary instead of whatever happens to be on `PATH`. For one-off use, run `npx fallow` without installing. Other channels (cargo, Docker, prebuilt binaries) are covered in the [installation guide](https://docs.fallow.tools/installation).
|
|
44
18
|
|
|
45
19
|
## Quick start
|
|
46
20
|
|
|
47
21
|
```bash
|
|
48
|
-
npx fallow
|
|
49
|
-
npx fallow audit
|
|
50
|
-
npx fallow health --score #
|
|
51
|
-
npx fallow
|
|
52
|
-
npx fallow dead-code # Cleanup-specific findings
|
|
22
|
+
npx fallow # Full pipeline: dead code + duplication + health
|
|
23
|
+
npx fallow audit # Gate only what a PR changed: verdict pass/warn/fail
|
|
24
|
+
npx fallow health --score # 0 to 100 health score with a letter grade
|
|
25
|
+
npx fallow dupes # Duplication; modes strict, mild (default), weak, semantic
|
|
53
26
|
npx fallow fix --dry-run # Preview automatic cleanup
|
|
54
27
|
```
|
|
55
28
|
|
|
56
|
-
##
|
|
57
|
-
|
|
58
|
-
- **Quality score** -- compact health score with grade and trend delta when snapshot history is enabled
|
|
59
|
-
- **PR risk** -- changed-code analysis with pass / warn / fail verdict and per-finding attribution
|
|
60
|
-
- **Hotspots** -- functions, files, and packages combining complexity, churn, size, and coupling
|
|
61
|
-
- **Duplication** -- clone families across four detection modes (strict, mild, weak, semantic)
|
|
62
|
-
- **Design-system styling** -- CSS, Sass/Less, CSS Modules, Tailwind/shadcn/CVA, StyleX/PandaCSS, vanilla-extract, styled-components, and Emotion consistency signals
|
|
63
|
-
- **Architecture** -- circular dependencies, boundary violations, re-export chains
|
|
64
|
-
- **Dependency hygiene** -- unused, unlisted, unresolved, duplicate, and type-only deps; pnpm catalog and overrides
|
|
65
|
-
- **Cleanup opportunities** -- unused files, exports, types, enum members, class members, stale suppressions
|
|
66
|
-
- **Runtime intelligence (optional)** -- hot paths, cold code, runtime-weighted health, stale flags
|
|
29
|
+
## Output and exit codes
|
|
67
30
|
|
|
68
|
-
|
|
31
|
+
Add `--format json --quiet` to any command for one typed JSON document on stdout. Exit code 1 means findings, not failure; 0 is clean (or an audit pass or warn verdict); 2 is a validation or runtime error, reported as a JSON error envelope rather than a stack trace.
|
|
69
32
|
|
|
70
|
-
|
|
33
|
+
Parsing the output in TypeScript? Import the typed shapes, version-pinned to the CLI you install:
|
|
71
34
|
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
fallow dupes --mode semantic # Catch clones with renamed variables
|
|
75
|
-
fallow dupes --threshold 5 # Fail CI if duplication exceeds 5%
|
|
76
|
-
fallow dupes --save-baseline # Save current duplication as baseline
|
|
35
|
+
```ts
|
|
36
|
+
import type { CheckOutput, FallowJsonOutput } from "fallow/types";
|
|
77
37
|
```
|
|
78
38
|
|
|
79
|
-
|
|
39
|
+
Every issue carries an `actions[]` array with an `auto_fixable` flag, so scripts and agents know which findings they can hand to `fallow fix`. The full contract lives at [docs.fallow.tools](https://docs.fallow.tools).
|
|
80
40
|
|
|
81
|
-
##
|
|
41
|
+
## What fallow reports
|
|
82
42
|
|
|
83
|
-
|
|
43
|
+
- Unused files, exports, types, enum and class members, and dependencies
|
|
44
|
+
- Circular dependencies and re-export cycles
|
|
45
|
+
- Code duplication as clone families, across four detection modes
|
|
46
|
+
- Complexity hotspots and a 0 to 100 health score
|
|
47
|
+
- Architecture boundary violations, with zero-config presets
|
|
48
|
+
- Design-system styling drift for CSS and CSS-in-JS (Sass/Less, CSS Modules, Tailwind, styled-components, Emotion, and more)
|
|
49
|
+
- A changed-file PR gate with per-finding attribution (`fallow audit`)
|
|
50
|
+
- Optional runtime intelligence: hot paths, cold code, runtime-weighted health, stale flags (licensed Fallow Runtime; a single local coverage capture is free)
|
|
84
51
|
|
|
85
|
-
-
|
|
86
|
-
- Why is this export considered used or unused?
|
|
87
|
-
- What changed in this PR?
|
|
88
|
-
- Which files are risky to touch?
|
|
89
|
-
- What duplicate siblings exist?
|
|
90
|
-
- What cleanup action is safest?
|
|
52
|
+
For head-to-head timings against [knip](https://knip.dev) and [jscpd](https://github.com/kucherenko/jscpd), see [BENCHMARKS.md](https://github.com/fallow-rs/fallow/blob/main/BENCHMARKS.md): fallow is faster than knip on smaller projects, knip is faster on several larger repos, and jscpd's Rust rewrite is faster at raw duplication scanning.
|
|
91
53
|
|
|
92
|
-
|
|
54
|
+
## Built for agents
|
|
93
55
|
|
|
94
|
-
|
|
56
|
+
Agents get structured repo truth instead of inferring everything from grep: who imports a symbol, why an export counts as used, what a PR changed, which cleanup action is safest.
|
|
95
57
|
|
|
96
|
-
|
|
58
|
+
The bundled `fallow-mcp` server lives in `node_modules/.bin/` when installed as a devDependency, so launch it through your package manager's runner:
|
|
97
59
|
|
|
98
60
|
```json
|
|
99
61
|
{
|
|
@@ -106,15 +68,22 @@ Agents that speak MCP can launch the bundled `fallow-mcp` server. Installed as a
|
|
|
106
68
|
}
|
|
107
69
|
```
|
|
108
70
|
|
|
109
|
-
Swap `npx` for `pnpm exec`
|
|
71
|
+
Swap `npx` for `pnpm exec` or `yarn` to match your package manager; a globally installed `fallow-mcp` works as `"command": "fallow-mcp"` directly. See the [MCP integration guide](https://docs.fallow.tools/integrations/mcp).
|
|
72
|
+
|
|
73
|
+
The package also ships a version-matched agent skill under `skills/fallow`, and `fallow/capabilities.json` mirrors `fallow schema` for tools that need CLI and issue-surface metadata without spawning the binary. TanStack Intent discovers both from `node_modules`:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
npx @tanstack/intent list
|
|
77
|
+
npx @tanstack/intent load fallow#fallow
|
|
78
|
+
```
|
|
110
79
|
|
|
111
80
|
## Framework support
|
|
112
81
|
|
|
113
|
-
|
|
82
|
+
Over 100 built-in framework plugins covering Next.js, Nuxt, Remix, Qwik, SvelteKit, Gatsby, Astro, Angular, NestJS, AdonisJS, Ember, Expo Router, Vite, Webpack, Vitest, Jest, Playwright, Cypress, Storybook, ESLint, TypeScript, Tailwind, UnoCSS, Prisma, Drizzle, Convex, Turborepo, Hardhat, and more. Entry points are auto-detected from `package.json`, so the first run needs no configuration.
|
|
114
83
|
|
|
115
84
|
## Configuration
|
|
116
85
|
|
|
117
|
-
|
|
86
|
+
Works out of the box. To customize, let [`fallow recommend`](https://docs.fallow.tools/cli/recommend) propose a config from the detected stack (read-only; `--format json` returns the full decision set for agents), run `fallow init`, or create a config file in your project root:
|
|
118
87
|
|
|
119
88
|
```jsonc
|
|
120
89
|
// .fallowrc.json
|
|
@@ -130,23 +99,13 @@ Create a config file in your project root, or run `fallow init`:
|
|
|
130
99
|
}
|
|
131
100
|
```
|
|
132
101
|
|
|
133
|
-
`$schema` gives editors autocomplete and validation and has no effect on
|
|
134
|
-
analysis. As an npm package, `fallow` always ships a version-aligned schema at
|
|
135
|
-
`./node_modules/fallow/schema.json`, which `fallow init` and
|
|
136
|
-
`fallow recommend` point at by default: offline, no editor trust prompt. If
|
|
137
|
-
you install fallow another way (cargo, homebrew, a bare binary) with no
|
|
138
|
-
`node_modules/fallow` to point at, use the remote fallback instead:
|
|
139
|
-
`https://raw.githubusercontent.com/fallow-rs/fallow/main/schema.json`. Some
|
|
140
|
-
editors, including VS Code, refuse to load a remote schema URL until you
|
|
141
|
-
explicitly trust the domain.
|
|
142
|
-
|
|
143
|
-
Also supports TOML (`fallow init --toml` creates `fallow.toml`).
|
|
102
|
+
`$schema` gives editors autocomplete and validation and has no effect on analysis. The npm package ships a version-aligned schema at `./node_modules/fallow/schema.json`, so validation works offline with no editor trust prompt. TOML works too: `fallow init --toml` creates `fallow.toml`. Full reference: [configuration overview](https://docs.fallow.tools/configuration/overview).
|
|
144
103
|
|
|
145
104
|
## Documentation
|
|
146
105
|
|
|
147
|
-
- [
|
|
106
|
+
- [docs.fallow.tools](https://docs.fallow.tools)
|
|
148
107
|
- [GitHub repository](https://github.com/fallow-rs/fallow)
|
|
149
|
-
- [Plugin
|
|
108
|
+
- [Plugin authoring guide](https://github.com/fallow-rs/fallow/blob/main/docs/plugin-authoring.md)
|
|
150
109
|
|
|
151
110
|
## License
|
|
152
111
|
|
package/capabilities.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fallow",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.6.0",
|
|
4
4
|
"manifest_version": "1",
|
|
5
5
|
"description": "Codebase analyzer for TypeScript/JavaScript: unused code, circular dependencies, code duplication, complexity hotspots, and architecture boundary violations",
|
|
6
6
|
"global_flags": [
|
|
@@ -51,6 +51,16 @@
|
|
|
51
51
|
"github-summary"
|
|
52
52
|
]
|
|
53
53
|
},
|
|
54
|
+
{
|
|
55
|
+
"name": "--pretty",
|
|
56
|
+
"type": "bool",
|
|
57
|
+
"required": false,
|
|
58
|
+
"description": "Indent JSON output for manual inspection. Requires the final output format to be JSON",
|
|
59
|
+
"possible_values": [
|
|
60
|
+
"true",
|
|
61
|
+
"false"
|
|
62
|
+
]
|
|
63
|
+
},
|
|
54
64
|
{
|
|
55
65
|
"name": "--quiet",
|
|
56
66
|
"type": "bool",
|
|
@@ -311,7 +321,7 @@
|
|
|
311
321
|
"name": "--save-regression-baseline",
|
|
312
322
|
"type": "string",
|
|
313
323
|
"required": false,
|
|
314
|
-
"description": "Save the current issue counts as a regression baseline"
|
|
324
|
+
"description": "Save the current issue counts as a regression baseline. Omit PATH to update regression.baseline in the discovered fallow config, or create .fallowrc.json when none exists. Provide PATH to write a standalone file"
|
|
315
325
|
},
|
|
316
326
|
{
|
|
317
327
|
"name": "--only",
|
|
@@ -1700,6 +1710,11 @@
|
|
|
1700
1710
|
}
|
|
1701
1711
|
]
|
|
1702
1712
|
},
|
|
1713
|
+
{
|
|
1714
|
+
"name": "audit-cache",
|
|
1715
|
+
"description": "Maintain reusable audit base-snapshot caches",
|
|
1716
|
+
"flags": []
|
|
1717
|
+
},
|
|
1703
1718
|
{
|
|
1704
1719
|
"name": "decision-surface",
|
|
1705
1720
|
"description": "Surface the consequential structural DECISIONS a change embeds (the apex of the review brief), each framed as a judgment question with the routed expert to ask",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fallow",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.6.0",
|
|
4
4
|
"description": "Codebase intelligence for TypeScript and JavaScript. Free static analysis of code and styles, optional paid runtime intelligence (Fallow Runtime). Quality, risk, architecture, dependencies, duplication, and design-system drift for humans, CI, and the agents writing your code. Zero-config framework support.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -84,16 +84,16 @@
|
|
|
84
84
|
"detect-libc": "2.1.2"
|
|
85
85
|
},
|
|
86
86
|
"devDependencies": {
|
|
87
|
-
"@tanstack/intent": "0.3.
|
|
87
|
+
"@tanstack/intent": "0.3.5"
|
|
88
88
|
},
|
|
89
89
|
"optionalDependencies": {
|
|
90
|
-
"@fallow-cli/darwin-arm64": "3.
|
|
91
|
-
"@fallow-cli/darwin-x64": "3.
|
|
92
|
-
"@fallow-cli/linux-x64-gnu": "3.
|
|
93
|
-
"@fallow-cli/linux-arm64-gnu": "3.
|
|
94
|
-
"@fallow-cli/linux-x64-musl": "3.
|
|
95
|
-
"@fallow-cli/linux-arm64-musl": "3.
|
|
96
|
-
"@fallow-cli/win32-arm64-msvc": "3.
|
|
97
|
-
"@fallow-cli/win32-x64-msvc": "3.
|
|
90
|
+
"@fallow-cli/darwin-arm64": "3.6.0",
|
|
91
|
+
"@fallow-cli/darwin-x64": "3.6.0",
|
|
92
|
+
"@fallow-cli/linux-x64-gnu": "3.6.0",
|
|
93
|
+
"@fallow-cli/linux-arm64-gnu": "3.6.0",
|
|
94
|
+
"@fallow-cli/linux-x64-musl": "3.6.0",
|
|
95
|
+
"@fallow-cli/linux-arm64-musl": "3.6.0",
|
|
96
|
+
"@fallow-cli/win32-arm64-msvc": "3.6.0",
|
|
97
|
+
"@fallow-cli/win32-x64-msvc": "3.6.0"
|
|
98
98
|
}
|
|
99
99
|
}
|
|
@@ -1,10 +1,61 @@
|
|
|
1
1
|
const test = require("node:test");
|
|
2
2
|
const assert = require("node:assert/strict");
|
|
3
|
+
const fs = require("node:fs");
|
|
3
4
|
const os = require("node:os");
|
|
4
5
|
const { spawnSync } = require("node:child_process");
|
|
5
6
|
const path = require("node:path");
|
|
6
7
|
|
|
7
8
|
const RUN_BINARY = path.join(__dirname, "run-binary.js");
|
|
9
|
+
const BIN_DIR = path.join(__dirname, "..", "bin");
|
|
10
|
+
|
|
11
|
+
function currentPlatformPackage() {
|
|
12
|
+
const { getPlatformPackage } = require("./platform-package");
|
|
13
|
+
if (process.platform !== "linux") {
|
|
14
|
+
return getPlatformPackage(process.platform, process.arch);
|
|
15
|
+
}
|
|
16
|
+
let libcFamily;
|
|
17
|
+
try {
|
|
18
|
+
libcFamily = require("detect-libc").familySync();
|
|
19
|
+
} catch {
|
|
20
|
+
libcFamily = undefined;
|
|
21
|
+
}
|
|
22
|
+
return getPlatformPackage(process.platform, process.arch, libcFamily);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function runLauncher(t, launcher, args) {
|
|
26
|
+
const work = fs.mkdtempSync(path.join(os.tmpdir(), "fallow-launcher-"));
|
|
27
|
+
t.after(() => fs.rmSync(work, { recursive: true, force: true }));
|
|
28
|
+
|
|
29
|
+
const pkg = currentPlatformPackage();
|
|
30
|
+
assert.ok(pkg, "the test host must map to a supported platform package");
|
|
31
|
+
const pkgDir = path.join(work, "node_modules", ...pkg.split("/"));
|
|
32
|
+
fs.mkdirSync(pkgDir, { recursive: true });
|
|
33
|
+
fs.writeFileSync(
|
|
34
|
+
path.join(pkgDir, "package.json"),
|
|
35
|
+
JSON.stringify({ name: pkg, version: "3.5.0" }),
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
const binary = path.join(pkgDir, "fallow");
|
|
39
|
+
fs.writeFileSync(
|
|
40
|
+
binary,
|
|
41
|
+
"#!/usr/bin/env node\n" +
|
|
42
|
+
'require("node:fs").writeFileSync(process.env.FALLOW_TEST_ARGS, JSON.stringify(process.argv.slice(2)));\n',
|
|
43
|
+
);
|
|
44
|
+
fs.chmodSync(binary, 0o755);
|
|
45
|
+
|
|
46
|
+
const argsFile = path.join(work, "args.json");
|
|
47
|
+
const result = spawnSync(process.execPath, [path.join(BIN_DIR, launcher), ...args], {
|
|
48
|
+
encoding: "utf8",
|
|
49
|
+
env: {
|
|
50
|
+
...process.env,
|
|
51
|
+
NODE_PATH: path.join(work, "node_modules"),
|
|
52
|
+
FALLOW_SKIP_BINARY_VERIFY: "1",
|
|
53
|
+
FALLOW_TEST_ARGS: argsFile,
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
assert.equal(result.status, 0, result.stderr);
|
|
57
|
+
return JSON.parse(fs.readFileSync(argsFile, "utf8"));
|
|
58
|
+
}
|
|
8
59
|
|
|
9
60
|
// Run a child that installs guardBrokenStdout, then emits a synthetic stdout
|
|
10
61
|
// 'error' with the given code. Node delivers a broken-pipe failure as exactly
|
|
@@ -91,3 +142,23 @@ test("exitCodeForChildFailure preserves status codes and maps signal deaths", ()
|
|
|
91
142
|
assert.equal(exitCodeForChildFailure({ status: null, signal: undefined }), 1);
|
|
92
143
|
assert.equal(exitCodeForChildFailure({ status: null, signal: "NOT_A_SIGNAL" }), 1);
|
|
93
144
|
});
|
|
145
|
+
|
|
146
|
+
test(
|
|
147
|
+
"fallow-lsp executes the multicall binary with the lsp-server subcommand",
|
|
148
|
+
{ skip: process.platform === "win32" },
|
|
149
|
+
(t) => {
|
|
150
|
+
assert.deepEqual(runLauncher(t, "fallow-lsp", ["--stdio"]), ["lsp-server", "--stdio"]);
|
|
151
|
+
},
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
test(
|
|
155
|
+
"fallow-mcp executes the multicall binary with the mcp-server subcommand",
|
|
156
|
+
{ skip: process.platform === "win32" },
|
|
157
|
+
(t) => {
|
|
158
|
+
assert.deepEqual(runLauncher(t, "fallow-mcp", ["--transport", "stdio"]), [
|
|
159
|
+
"mcp-server",
|
|
160
|
+
"--transport",
|
|
161
|
+
"stdio",
|
|
162
|
+
]);
|
|
163
|
+
},
|
|
164
|
+
);
|
package/skills/fallow/SKILL.md
CHANGED
|
@@ -45,10 +45,10 @@ cargo install fallow-cli # build from source
|
|
|
45
45
|
|
|
46
46
|
## Agent Rules
|
|
47
47
|
|
|
48
|
-
1. **Always use `--format json --quiet 2>/dev/null`** for machine-readable output. The `2>/dev/null` discards stderr so progress messages and threshold warnings don't corrupt the JSON on stdout. Never use `2>&1`
|
|
48
|
+
1. **Always use `--format json --quiet 2>/dev/null`** for machine-readable output and parse it as JSON. Compact JSON is the default; never depend on whitespace or add `--pretty` in agent pipelines. The `2>/dev/null` discards stderr so progress messages and threshold warnings don't corrupt the JSON on stdout. Never use `2>&1`
|
|
49
49
|
2. **Always append `|| true`** to every fallow command. Exit code 1 means "issues found" (normal), not a runtime error. Without `|| true`, the Bash tool treats exit 1 as failure and cancels parallel commands. Only exit code 2 is a real error (invalid config, parse failure)
|
|
50
50
|
3. **Use `--explain`** to include a `_meta` object in JSON output with metric definitions, ranges, and interpretation hints. In human format, `--explain` prints a `Description:` line under each section header.
|
|
51
|
-
4. **Use the root `kind` field** to identify typed JSON envelopes (`dead-code`, `dead-code-grouped`, `health`, `dupes`, `combined`, `audit`, etc.).
|
|
51
|
+
4. **Use the root `kind` field** to identify typed JSON envelopes (`dead-code`, `dead-code-grouped`, `health`, `dupes`, `combined`, `audit`, etc.).
|
|
52
52
|
5. **Use issue type filters** (`--unused-exports`, `--unused-files`, etc.) to limit output scope
|
|
53
53
|
6. **Always `--dry-run` before `fix`**, then `fix --yes` to apply
|
|
54
54
|
7. **All output paths are relative** to the project root
|
|
@@ -109,6 +109,7 @@ Route by intent before reaching for the big analysis commands. Same matrix as `f
|
|
|
109
109
|
| `suppressions` | List active fallow-ignore suppression markers (read-only inventory) | `--file` |
|
|
110
110
|
| `explain` | Explain one issue type without running analysis | `<issue-type>`, `--format json` |
|
|
111
111
|
| `audit` | Combined dead-code + complexity + duplication + styling for changed files, returns a verdict; `fallow review` is an alias for `fallow audit --brief` (advisory orientation brief, always exits 0) | `--base`, `--gate`, `--brief`, `--max-decisions`, `--walkthrough-guide`, `--walkthrough-file`, `--show-deprioritized`, `--production`, `--production-dead-code`, `--production-health`, `--production-dupes`, `--workspace`, `--changed-workspaces`, `--ci`, `--fail-on-issues`, `--explain`, `--explain-skipped`, `--dead-code-baseline`, `--health-baseline`, `--dupes-baseline`, `--max-crap`, `--coverage`, `--coverage-root`, `--no-css`, `--css-deep`, `--no-css-deep`, `--include-entry-exports` |
|
|
112
|
+
| `audit-cache` | Maintain reusable audit base-snapshot caches | |
|
|
112
113
|
| `decision-surface` | Surface the consequential structural DECISIONS a change embeds (the apex of the review brief), each framed as a judgment question with the routed expert to ask | `--max-decisions` |
|
|
113
114
|
| `impact` | Show what fallow has done for you: how many issues it is surfacing, the trend since the last recorded run, and how many commits it contained at the pre-commit gate | `--all`, `--sort`, `--limit` |
|
|
114
115
|
| `security` | Surface opt-in local security candidates for agent verification (not confirmed vulnerabilities). Rule families include the graph rule `client-server-leak`, a data-driven `tainted-sink` catalogue, and the include-required `hardcoded-secret` category for provider-prefix credentials and high-entropy literals assigned to secret-shaped identifiers. Most catalogue rows require non-literal input; narrowly literal-aware rows flag deterministic unsafe literals. Rules default off; suppress a file with `// fallow-ignore-file security-sink`; scope categories with `security.categories`. Add project-local request object names with `security.requestReceivers`; it extends the built-in `req` / `request` / `ctx` / `context` / `event` allowlist for HTTP `query`, `params`, and `body` reads. `hardcoded-secret` runs only when listed in `security.categories.include`. | `--format human\|json\|sarif`, `--changed-since`, `--file`, `--diff-file`, `--workspace`, `--changed-workspaces`, `--surface`, `--ci`, `--fail-on-issues`, `--sarif-file`, `--summary` |
|
|
@@ -350,15 +351,17 @@ Adds a per-decision-point `contributions[]` array to every complexity finding (e
|
|
|
350
351
|
### Gate CI on regressions (baselines)
|
|
351
352
|
```bash
|
|
352
353
|
# 1. Save the current issue counts as a regression baseline
|
|
353
|
-
fallow dead-code --format json --quiet --save-regression-baseline
|
|
354
|
+
fallow dead-code --format json --quiet --save-regression-baseline
|
|
354
355
|
# 2. In CI: fail only if issues increase beyond tolerance
|
|
355
|
-
fallow dead-code --format json --quiet --
|
|
356
|
+
fallow dead-code --format json --quiet --fail-on-regression --tolerance 0
|
|
356
357
|
# Identity-based baseline (fail only on NEW findings, not raw counts)
|
|
357
358
|
fallow dead-code --format json --quiet --save-baseline .fallow/snapshot.json
|
|
358
359
|
fallow dead-code --format json --quiet --baseline .fallow/snapshot.json
|
|
359
360
|
```
|
|
360
361
|
|
|
361
|
-
`--save-regression-baseline` / `--regression-baseline` / `--fail-on-regression` / `--tolerance` are count-based gates
|
|
362
|
+
`--save-regression-baseline` / `--regression-baseline` / `--fail-on-regression` / `--tolerance` are count-based gates for `dead-code` and bare combined mode. `--save-baseline` / `--baseline` are identity-based (track finding identity, fail on new). `audit` rejects the global baseline flags and uses `--dead-code-baseline` / `--health-baseline` / `--dupes-baseline` instead.
|
|
363
|
+
|
|
364
|
+
With no path, `--save-regression-baseline` updates `regression.baseline` in the discovered fallow config, or creates `.fallowrc.json` when none exists. Pass a path only when a standalone baseline file is preferred.
|
|
362
365
|
|
|
363
366
|
### Explain an issue type without running analysis
|
|
364
367
|
```bash
|
|
@@ -130,8 +130,9 @@ fallow dead-code --format json --quiet --trace src/utils.ts:myFunction
|
|
|
130
130
|
fallow dead-code --format json --quiet --save-baseline fallow-baselines/dead-code.json
|
|
131
131
|
fallow dead-code --format json --quiet --baseline fallow-baselines/dead-code.json --fail-on-issues
|
|
132
132
|
|
|
133
|
-
# Regression detection:
|
|
133
|
+
# Regression detection: update regression.baseline in the discovered config
|
|
134
134
|
fallow dead-code --format json --quiet --save-regression-baseline
|
|
135
|
+
# Then compare on PRs
|
|
135
136
|
fallow dead-code --format json --quiet --fail-on-regression --tolerance 2%
|
|
136
137
|
|
|
137
138
|
# Scope to specific files (e.g., lint-staged)
|
|
@@ -509,7 +510,7 @@ fallow health --format json --quiet --trend
|
|
|
509
510
|
{
|
|
510
511
|
"kind": "health",
|
|
511
512
|
"schema_version": 7,
|
|
512
|
-
"version": "3.
|
|
513
|
+
"version": "3.6.0",
|
|
513
514
|
"elapsed_ms": 32,
|
|
514
515
|
"summary": {
|
|
515
516
|
"files_analyzed": 482,
|
|
@@ -907,7 +908,7 @@ fallow audit \
|
|
|
907
908
|
{
|
|
908
909
|
"kind": "audit",
|
|
909
910
|
"schema_version": 7,
|
|
910
|
-
"version": "3.
|
|
911
|
+
"version": "3.6.0",
|
|
911
912
|
"command": "audit",
|
|
912
913
|
"verdict": "fail",
|
|
913
914
|
"changed_files_count": 12,
|
|
@@ -982,7 +983,7 @@ fallow flags --format json --quiet --workspace my-package
|
|
|
982
983
|
```json
|
|
983
984
|
{
|
|
984
985
|
"schema_version": 7,
|
|
985
|
-
"version": "3.
|
|
986
|
+
"version": "3.6.0",
|
|
986
987
|
"elapsed_ms": 116,
|
|
987
988
|
"feature_flags": [],
|
|
988
989
|
"total_flags": 0
|
|
@@ -1083,7 +1084,7 @@ fallow security --gate newly-reachable --changed-since origin/main
|
|
|
1083
1084
|
{
|
|
1084
1085
|
"kind": "security",
|
|
1085
1086
|
"schema_version": "4",
|
|
1086
|
-
"version": "3.
|
|
1087
|
+
"version": "3.6.0",
|
|
1087
1088
|
"elapsed_ms": 42,
|
|
1088
1089
|
"config": {
|
|
1089
1090
|
"rules": {
|
|
@@ -1112,7 +1113,7 @@ fallow security --gate newly-reachable --changed-since origin/main
|
|
|
1112
1113
|
{
|
|
1113
1114
|
"kind": "security",
|
|
1114
1115
|
"schema_version": "4",
|
|
1115
|
-
"version": "3.
|
|
1116
|
+
"version": "3.6.0",
|
|
1116
1117
|
"elapsed_ms": 42,
|
|
1117
1118
|
"config": {
|
|
1118
1119
|
"rules": {
|
|
@@ -1637,6 +1638,7 @@ Available on all commands:
|
|
|
1637
1638
|
| `-c, --config` | `string` | - | Config file path |
|
|
1638
1639
|
| `--allow-remote-extends` | `bool` | `false` | Allow trusted config files to extend HTTPS URLs |
|
|
1639
1640
|
| `-f, --format` | `human\|json\|sarif\|compact\|markdown\|codeclimate\|pr-comment-github\|pr-comment-gitlab\|review-github\|review-gitlab\|badge\|github-annotations\|github-summary` | `human` | Output format (alias: --output) |
|
|
1641
|
+
| `--pretty` | `bool` | `false` | Indent JSON output for manual inspection. Requires the final output format to be JSON |
|
|
1640
1642
|
| `-q, --quiet` | `bool` | `false` | Suppress progress output |
|
|
1641
1643
|
| `--no-cache` | `bool` | `false` | Disable incremental caching |
|
|
1642
1644
|
| `--threads` | `string` | - | Number of parser threads |
|
|
@@ -1667,8 +1669,8 @@ Available on all commands:
|
|
|
1667
1669
|
| `--report-path-prefix` | `string` | - | Prefix prepended to every path in the CI-facing formats (`github-annotations`, `github-summary`, `codeclimate`, `review-github`, `review-gitlab`). CI platforms address files by repository-root-relative path, so when the analyzed project lives in a subdirectory (e.g. `packages/app/`), paths need that offset. fallow detects the offset via the git toplevel automatically; this flag overrides the detection. Pass an empty string to disable rebasing and emit paths relative to `--root` |
|
|
1668
1670
|
| `--fail-on-regression` | `bool` | `false` | Fail if issue count increased beyond tolerance vs a regression baseline |
|
|
1669
1671
|
| `--tolerance` | `string` | `0` | Allowed increase: `"2%"` (percentage) or `"5"` (absolute). Default: `"0"` |
|
|
1670
|
-
| `--regression-baseline` | `string` | - | Path to regression baseline file
|
|
1671
|
-
| `--save-regression-baseline` | `string` | - | Save current issue counts
|
|
1672
|
+
| `--regression-baseline` | `string` | - | Path to a standalone regression baseline file. Without it, fallow uses `regression.baseline` from the config |
|
|
1673
|
+
| `--save-regression-baseline` | `string` | - | Save current issue counts. With no path, update `regression.baseline` in the discovered fallow config or create `.fallowrc.json`; with a path, write a standalone baseline file |
|
|
1672
1674
|
| `--only` | `dead-code\|dupes\|health` | - | Run only specific analyses (e.g., `--only dead-code,dupes`). Values: `dead-code` (alias: `check`), `dupes`, `health` |
|
|
1673
1675
|
| `--skip` | `dead-code\|dupes\|health` | - | Skip specific analyses (e.g., `--skip health`). Values: `dead-code` (alias: `check`), `dupes`, `health` |
|
|
1674
1676
|
| `--dupes-mode` | `strict\|mild\|weak\|semantic` | - | Override duplication detection mode in combined mode |
|
|
@@ -1766,7 +1768,7 @@ Set `FALLOW_FORMAT=json` and `FALLOW_QUIET=1` in your agent environment to avoid
|
|
|
1766
1768
|
| Format | Description | Use Case |
|
|
1767
1769
|
|--------|-------------|----------|
|
|
1768
1770
|
| `human` | Colored terminal output | Interactive use |
|
|
1769
|
-
| `json` |
|
|
1771
|
+
| `json` | Compact machine-readable JSON by default; add `--pretty` for indented manual inspection | Agent integration, CI pipelines |
|
|
1770
1772
|
| `sarif` | Static Analysis Results Interchange Format | GitHub Code Scanning, SARIF-compatible tools |
|
|
1771
1773
|
| `compact` | Grep-friendly: one issue per line. Dupes lines include `code-duplication:path:start-end:fingerprint=dup:<id>,...` | Quick filtering |
|
|
1772
1774
|
| `markdown` | Markdown tables | Documentation, PR comments |
|
|
@@ -1836,7 +1838,7 @@ The HTTP layer mirrors the bash `gh_api_retry` / `curl_retry` helpers: `FALLOW_A
|
|
|
1836
1838
|
{
|
|
1837
1839
|
"kind": "dead-code",
|
|
1838
1840
|
"schema_version": 7,
|
|
1839
|
-
"version": "3.
|
|
1841
|
+
"version": "3.6.0",
|
|
1840
1842
|
"elapsed_ms": 45,
|
|
1841
1843
|
"total_issues": 12,
|
|
1842
1844
|
"entry_points": {
|
|
@@ -1996,7 +1998,7 @@ When `--baseline` is used in combined output, the JSON includes a `baseline_delt
|
|
|
1996
1998
|
{
|
|
1997
1999
|
"kind": "dupes",
|
|
1998
2000
|
"schema_version": 7,
|
|
1999
|
-
"version": "3.
|
|
2001
|
+
"version": "3.6.0",
|
|
2000
2002
|
"elapsed_ms": 82,
|
|
2001
2003
|
"total_clones": 15,
|
|
2002
2004
|
"total_lines_duplicated": 230,
|
|
@@ -2040,11 +2042,11 @@ When running `fallow` with no subcommand (all analyses), the JSON output combine
|
|
|
2040
2042
|
{
|
|
2041
2043
|
"kind": "combined",
|
|
2042
2044
|
"schema_version": 7,
|
|
2043
|
-
"version": "3.
|
|
2045
|
+
"version": "3.6.0",
|
|
2044
2046
|
"elapsed_ms": 159,
|
|
2045
2047
|
"check": {
|
|
2046
2048
|
"schema_version": 7,
|
|
2047
|
-
"version": "3.
|
|
2049
|
+
"version": "3.6.0",
|
|
2048
2050
|
"elapsed_ms": 45,
|
|
2049
2051
|
"total_issues": 12,
|
|
2050
2052
|
"unused_files": [],
|
|
@@ -7,9 +7,9 @@
|
|
|
7
7
|
* `npm/fallow/types/output-contract.d.ts` (published as `fallow/types`).
|
|
8
8
|
*
|
|
9
9
|
* To change a shape:
|
|
10
|
-
* 1.
|
|
11
|
-
*
|
|
12
|
-
*
|
|
10
|
+
* 1. Find the Rust owner through `derived_definition_names()` and its
|
|
11
|
+
* imports in `crates/cli/src/bin/schema_emit.rs`, then edit that type.
|
|
12
|
+
* Rust is the runtime source of truth for the JSON output.
|
|
13
13
|
* 2. Regenerate `docs/output-schema.json` with
|
|
14
14
|
* `cargo run -p fallow-cli --features schema-emit --bin fallow-schema-emit`.
|
|
15
15
|
* The schema-emit drift tests compare the committed schema against the
|
|
@@ -10466,12 +10466,10 @@ export type RefactoringTarget = Omit<RefactoringTargetFinding, "actions">;
|
|
|
10466
10466
|
// import the bare names from `fallow/types` would break at upgrade time
|
|
10467
10467
|
// otherwise.
|
|
10468
10468
|
//
|
|
10469
|
-
// Stability commitment:
|
|
10470
|
-
//
|
|
10471
|
-
//
|
|
10472
|
-
//
|
|
10473
|
-
// CHANGELOG headline) preceding the removal. New code should prefer the
|
|
10474
|
-
// `*Finding` wrapper names. Full public-consumer policy:
|
|
10469
|
+
// Stability commitment: legacy output aliases remain supported throughout v3.
|
|
10470
|
+
// Removing them requires an explicit deprecation period and a future major
|
|
10471
|
+
// release. New code should prefer the `*Finding` wrapper names. Full
|
|
10472
|
+
// public-consumer policy:
|
|
10475
10473
|
// https://github.com/fallow-rs/fallow/blob/main/docs/backwards-compatibility.md
|
|
10476
10474
|
//
|
|
10477
10475
|
|