fallow 3.16.0 → 3.18.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 +1 -1
- package/capabilities.json +30 -16
- package/issue-registry.json +2 -2
- package/package.json +10 -10
- package/schema.json +4 -4
- package/scripts/lazy-verify.js +16 -1
- package/scripts/lazy-verify.test.js +79 -100
- package/scripts/run-binary.js +30 -10
- package/scripts/run-binary.test.js +22 -1
- package/scripts/sentinel-path.test.js +9 -2
- package/scripts/verify-binary.test.js +50 -4
- package/skills/fallow/SKILL.md +10 -14
- package/skills/fallow/references/cli-reference.md +5 -5
- package/skills/fallow/references/gotchas.md +1 -1
- package/skills/fallow/references/mcp.md +24 -1
- package/skills/fallow/references/patterns.md +2 -2
- package/types/output-contract.d.ts +164 -46
|
@@ -162,16 +162,32 @@ test("resolveTypeAwareCompanion accepts only an exact matching package", (t) =>
|
|
|
162
162
|
test("childEnvironment marks launcher-wired companions as npm-wrapper", (t) => {
|
|
163
163
|
const { childEnvironment } = require(RUN_BINARY);
|
|
164
164
|
const previousBin = process.env.FALLOW_TYPE_AWARE_BIN;
|
|
165
|
+
const previousScript = process.env.FALLOW_TYPE_AWARE_SCRIPT;
|
|
165
166
|
delete process.env.FALLOW_TYPE_AWARE_BIN;
|
|
167
|
+
delete process.env.FALLOW_TYPE_AWARE_SCRIPT;
|
|
166
168
|
t.after(() => {
|
|
167
169
|
if (previousBin === undefined) delete process.env.FALLOW_TYPE_AWARE_BIN;
|
|
168
170
|
else process.env.FALLOW_TYPE_AWARE_BIN = previousBin;
|
|
171
|
+
if (previousScript === undefined) delete process.env.FALLOW_TYPE_AWARE_SCRIPT;
|
|
172
|
+
else process.env.FALLOW_TYPE_AWARE_SCRIPT = previousScript;
|
|
169
173
|
});
|
|
170
174
|
|
|
171
|
-
const env = childEnvironment("3.8.0", () => "/tmp/fallow-type-aware.mjs"
|
|
175
|
+
const env = childEnvironment("3.8.0", () => "/tmp/fallow-type-aware.mjs", {
|
|
176
|
+
platform: "linux",
|
|
177
|
+
execPath: "/usr/bin/node",
|
|
178
|
+
});
|
|
172
179
|
assert.equal(env.FALLOW_TYPE_AWARE_BIN, "/tmp/fallow-type-aware.mjs");
|
|
180
|
+
assert.equal(env.FALLOW_TYPE_AWARE_SCRIPT, undefined);
|
|
173
181
|
assert.equal(env.FALLOW_TYPE_AWARE_BIN_SOURCE, "npm-wrapper");
|
|
174
182
|
|
|
183
|
+
const windowsEnv = childEnvironment("3.8.0", () => "C:\\pkg\\fallow-type-aware.mjs", {
|
|
184
|
+
platform: "win32",
|
|
185
|
+
execPath: "C:\\Program Files\\nodejs\\node.exe",
|
|
186
|
+
});
|
|
187
|
+
assert.equal(windowsEnv.FALLOW_TYPE_AWARE_BIN, "C:\\Program Files\\nodejs\\node.exe");
|
|
188
|
+
assert.equal(windowsEnv.FALLOW_TYPE_AWARE_SCRIPT, "C:\\pkg\\fallow-type-aware.mjs");
|
|
189
|
+
assert.equal(windowsEnv.FALLOW_TYPE_AWARE_BIN_SOURCE, "npm-wrapper");
|
|
190
|
+
|
|
175
191
|
// No resolvable companion: the environment passes through untouched.
|
|
176
192
|
const untouched = childEnvironment("3.8.0", () => undefined);
|
|
177
193
|
assert.equal(untouched, process.env);
|
|
@@ -181,14 +197,19 @@ test("childEnvironment marks launcher-wired companions as npm-wrapper", (t) => {
|
|
|
181
197
|
test("childEnvironment leaves a user-set override unmarked", (t) => {
|
|
182
198
|
const { childEnvironment } = require(RUN_BINARY);
|
|
183
199
|
const previousBin = process.env.FALLOW_TYPE_AWARE_BIN;
|
|
200
|
+
const previousScript = process.env.FALLOW_TYPE_AWARE_SCRIPT;
|
|
184
201
|
process.env.FALLOW_TYPE_AWARE_BIN = "/opt/custom-sidecar";
|
|
202
|
+
process.env.FALLOW_TYPE_AWARE_SCRIPT = "/opt/custom-sidecar.mjs";
|
|
185
203
|
t.after(() => {
|
|
186
204
|
if (previousBin === undefined) delete process.env.FALLOW_TYPE_AWARE_BIN;
|
|
187
205
|
else process.env.FALLOW_TYPE_AWARE_BIN = previousBin;
|
|
206
|
+
if (previousScript === undefined) delete process.env.FALLOW_TYPE_AWARE_SCRIPT;
|
|
207
|
+
else process.env.FALLOW_TYPE_AWARE_SCRIPT = previousScript;
|
|
188
208
|
});
|
|
189
209
|
|
|
190
210
|
const env = childEnvironment("3.8.0", () => "/tmp/fallow-type-aware.mjs");
|
|
191
211
|
assert.equal(env, process.env);
|
|
212
|
+
assert.equal(env.FALLOW_TYPE_AWARE_SCRIPT, "/opt/custom-sidecar.mjs");
|
|
192
213
|
assert.equal(env.FALLOW_TYPE_AWARE_BIN_SOURCE, undefined);
|
|
193
214
|
});
|
|
194
215
|
|
|
@@ -77,7 +77,7 @@ test("resolveSentinelPath falls back to FALLOW_VERIFY_CACHE_DIR when platform pk
|
|
|
77
77
|
const cacheDir = mkTmp();
|
|
78
78
|
try {
|
|
79
79
|
const result = resolveSentinelPath({
|
|
80
|
-
platformPkgDir: "
|
|
80
|
+
platformPkgDir: path.join(cacheDir, "missing-platform-package"),
|
|
81
81
|
packageName: "@fallow-cli/darwin-arm64",
|
|
82
82
|
env: { FALLOW_VERIFY_CACHE_DIR: cacheDir },
|
|
83
83
|
});
|
|
@@ -220,12 +220,19 @@ test("resolveSentinelPath honors injected isWritable + ensureDir for full test i
|
|
|
220
220
|
test("resolveSentinelPath skips cache-dir-env when ensureDir fails for it", () => {
|
|
221
221
|
// FALLOW_VERIFY_CACHE_DIR points at a non-creatable path, XDG points at a
|
|
222
222
|
// creatable one. Confirm the resolver moves past the env override.
|
|
223
|
+
//
|
|
224
|
+
// A directory nested under a regular FILE is the portable way to make mkdir
|
|
225
|
+
// fail: `/dev/null/inside/a/file` only refuses on POSIX, and on Windows the
|
|
226
|
+
// recursive mkdir succeeds against the current drive, which both defeats the
|
|
227
|
+
// assertion and creates C:\dev outside any temp directory.
|
|
223
228
|
const homeDir = mkTmp();
|
|
229
|
+
const blocker = path.join(homeDir, "not-a-directory");
|
|
230
|
+
fs.writeFileSync(blocker, "");
|
|
224
231
|
try {
|
|
225
232
|
const result = resolveSentinelPath({
|
|
226
233
|
platformPkgDir: undefined,
|
|
227
234
|
packageName: "@fallow-cli/darwin-arm64",
|
|
228
|
-
env: { FALLOW_VERIFY_CACHE_DIR: "
|
|
235
|
+
env: { FALLOW_VERIFY_CACHE_DIR: path.join(blocker, "inside", "a", "file") },
|
|
229
236
|
homedir: homeDir,
|
|
230
237
|
platform: "darwin",
|
|
231
238
|
});
|
|
@@ -239,10 +239,20 @@ test("verifyBinaryAt uses the embedded production public key", () => {
|
|
|
239
239
|
}
|
|
240
240
|
});
|
|
241
241
|
|
|
242
|
+
// Mirror binaryTargetsForPlatform: the suffix comes from the platformId under
|
|
243
|
+
// test, never from the live process.platform. A `dirOverride` run without an
|
|
244
|
+
// explicit platformId is labelled "test-platform" by
|
|
245
|
+
// resolvePlatformPackageForVerify, so the binary it looks for is plain `fallow`
|
|
246
|
+
// on every host -- a fixture keyed off process.platform writes `fallow.exe` on a
|
|
247
|
+
// Windows host and the verify then finds nothing.
|
|
248
|
+
function extForPlatformId(platformId) {
|
|
249
|
+
return typeof platformId === "string" && platformId.startsWith("win32") ? ".exe" : "";
|
|
250
|
+
}
|
|
251
|
+
|
|
242
252
|
function makePlatformDir(privateKey, options) {
|
|
243
253
|
const opts = options || {};
|
|
244
254
|
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "fallow-vbtest-"));
|
|
245
|
-
const ext =
|
|
255
|
+
const ext = extForPlatformId(opts.platformId);
|
|
246
256
|
for (const base of ["fallow"]) {
|
|
247
257
|
const binaryPath = path.join(dir, `${base}${ext}`);
|
|
248
258
|
const content = Buffer.from(`mock ${base} contents`);
|
|
@@ -328,6 +338,42 @@ test("verifyInstalled with dirOverride returns ok when every binary verifies", a
|
|
|
328
338
|
assert.equal(result.package, "<override>");
|
|
329
339
|
});
|
|
330
340
|
|
|
341
|
+
// binaryTargetsForPlatform reads windows-ness off the platformId so a Windows
|
|
342
|
+
// verify can be synthesized anywhere. Nothing exercised that, which left the
|
|
343
|
+
// `.exe` target unverified on Linux CI and unverified on Windows too.
|
|
344
|
+
test("verifyInstalled verifies the .exe target for a win32 platformId on any host", async (t) => {
|
|
345
|
+
const { privateKey, rawPub } = makeKeypair();
|
|
346
|
+
const platformId = "win32-x64-msvc";
|
|
347
|
+
const dir = makePlatformDir(privateKey, { platformId });
|
|
348
|
+
t.after(() => cleanup(dir));
|
|
349
|
+
assert.ok(fs.existsSync(path.join(dir, "fallow.exe")), "fixture must write the .exe target");
|
|
350
|
+
|
|
351
|
+
const result = await verifyInstalled({
|
|
352
|
+
dirOverride: dir,
|
|
353
|
+
platformId,
|
|
354
|
+
verifyFn: (p) => _verifyWithKey(p, rawPub),
|
|
355
|
+
digestProvider: makeDigestProvider(dir),
|
|
356
|
+
});
|
|
357
|
+
assert.equal(result.ok, true);
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
test("verifyInstalled reports the .exe name when a win32 signature is absent", async (t) => {
|
|
361
|
+
const { privateKey, rawPub } = makeKeypair();
|
|
362
|
+
const platformId = "win32-arm64-msvc";
|
|
363
|
+
const dir = makePlatformDir(privateKey, { platformId, skipSigFor: "fallow" });
|
|
364
|
+
t.after(() => cleanup(dir));
|
|
365
|
+
|
|
366
|
+
const result = await verifyInstalled({
|
|
367
|
+
dirOverride: dir,
|
|
368
|
+
platformId,
|
|
369
|
+
verifyFn: (p) => _verifyWithKey(p, rawPub),
|
|
370
|
+
digestProvider: makeDigestProvider(dir),
|
|
371
|
+
});
|
|
372
|
+
assert.equal(result.ok, false);
|
|
373
|
+
assert.equal(result.code, "sig-missing");
|
|
374
|
+
assert.match(result.message, /fallow\.exe/);
|
|
375
|
+
});
|
|
376
|
+
|
|
331
377
|
test("verifyInstalled resolves a global npm install from the fallow package directory", async (t) => {
|
|
332
378
|
const pkg = currentPlatformPackage();
|
|
333
379
|
if (!pkg) {
|
|
@@ -460,8 +506,8 @@ test("verifyInstalled honors FALLOW_SKIP_BINARY_VERIFY", async (t) => {
|
|
|
460
506
|
assert.equal(result.skipped, true);
|
|
461
507
|
});
|
|
462
508
|
|
|
463
|
-
function computeDigestsForDir(dir) {
|
|
464
|
-
const ext =
|
|
509
|
+
function computeDigestsForDir(dir, platformId) {
|
|
510
|
+
const ext = extForPlatformId(platformId);
|
|
465
511
|
const out = {};
|
|
466
512
|
for (const base of ["fallow"]) {
|
|
467
513
|
const fileName = `${base}${ext}`;
|
|
@@ -581,7 +627,7 @@ test("verifyInstalled returns digest-mismatch when the embedded digest disagrees
|
|
|
581
627
|
const { privateKey, rawPub } = makeKeypair();
|
|
582
628
|
const dir = makePlatformDir(privateKey);
|
|
583
629
|
t.after(() => cleanup(dir));
|
|
584
|
-
const ext =
|
|
630
|
+
const ext = extForPlatformId();
|
|
585
631
|
writeManifest(dir, {
|
|
586
632
|
name: "@fallow-cli/x",
|
|
587
633
|
version: "1.0.0",
|
package/skills/fallow/SKILL.md
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: fallow
|
|
3
|
-
description: Codebase intelligence for TypeScript and JavaScript. Static analysis of code and styles reports changed-code risk, cleanup opportunities, duplication, circular dependencies, complexity hotspots, architecture boundaries, design-system drift, feature flags, and opt-in security candidates. Runtime coverage can merge production execution data for hot-path review, cold-path deletion confidence, and stale-flag evidence.
|
|
3
|
+
description: Codebase intelligence for TypeScript and JavaScript. Static analysis of code and styles reports changed-code risk, cleanup opportunities, duplication, circular dependencies, complexity hotspots, architecture boundaries, design-system drift, feature flags, and opt-in security candidates. Runtime coverage can merge production execution data for hot-path review, cold-path deletion confidence, and stale-flag evidence. Broad framework plugin coverage, zero configuration, sub-second static analysis. Use when asked to audit PR risk, find unused code or dependencies, detect duplicates, check styling consistency, inspect architecture boundaries, merge runtime coverage, auto-fix supported issues, or run fallow.
|
|
4
4
|
license: MIT
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Fallow: codebase intelligence for TypeScript and JavaScript
|
|
8
8
|
|
|
9
|
-
Codebase intelligence for TypeScript and JavaScript. The static layer analyzes code and styles and reports quality, changed-code risk, cleanup opportunities, circular dependencies, code duplication, complexity hotspots, architecture boundary violations, design-system styling drift, feature flag patterns, and opt-in security candidates. Runtime coverage merges production execution data into the same `fallow health` report for hot-path review, cold-path deletion confidence, and stale-flag evidence, with a single local capture available by default and continuous/cloud runtime monitoring available as an optional mode.
|
|
9
|
+
Codebase intelligence for TypeScript and JavaScript. The static layer analyzes code and styles and reports quality, changed-code risk, cleanup opportunities, circular dependencies, code duplication, complexity hotspots, architecture boundary violations, design-system styling drift, feature flag patterns, and opt-in security candidates. Runtime coverage merges production execution data into the same `fallow health` report for hot-path review, cold-path deletion confidence, and stale-flag evidence, with a single local capture available by default and continuous/cloud runtime monitoring available as an optional mode. Broad framework plugin coverage, zero configuration, sub-second static analysis.
|
|
10
10
|
|
|
11
11
|
## When to Use
|
|
12
12
|
- Find cleanup opportunities: unused files, exports, types, members, dependencies, or stale flags.
|
|
@@ -42,8 +42,8 @@ cargo install fallow-cli # build from source
|
|
|
42
42
|
|
|
43
43
|
## Agent Rules
|
|
44
44
|
|
|
45
|
-
1. **Always use `--format json --quiet
|
|
46
|
-
2. **
|
|
45
|
+
1. **Always use `--format json --quiet`** for machine-readable output and parse stdout as JSON. Compact JSON is the default; never depend on whitespace or add `--pretty` in agent pipelines. Keep stderr separate so diagnostics remain visible; never merge it into the JSON stream with `2>&1`.
|
|
46
|
+
2. **Preserve and interpret the exit status.** Codes 0 and 1 are successful analysis outcomes: 0 is clean and 1 means findings. Treat every other code according to `fallow schema.exit_codes`. Do not force a successful status, because that hides validation, license, setup, network, and security-gate outcomes.
|
|
47
47
|
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.
|
|
48
48
|
4. **Use the root `kind` field** to identify typed JSON envelopes (`dead-code`, `dead-code-grouped`, `health`, `dupes`, `combined`, `audit`, etc.).
|
|
49
49
|
5. **Use issue type filters** (`--unused-exports`, `--unused-files`, etc.) to limit output scope
|
|
@@ -160,8 +160,8 @@ Run `fallow <command> --help` for the full flag list per command (see also refer
|
|
|
160
160
|
| `unused-catalog-entry` | `--unused-catalog-entries` | yes | - | `pnpm-workspace.yaml` entries no workspace package.json references via `catalog:` (default `warn`) |
|
|
161
161
|
| `empty-catalog-group` | `--empty-catalog-groups` | - | - | Named `catalogs.<name>:` groups in `pnpm-workspace.yaml` with no entries. Top-level `catalog:` placeholders are ignored. Default `warn`. |
|
|
162
162
|
| `unresolved-catalog-reference` | `--unresolved-catalog-references` | - | - | `package.json` references to `catalog:` / `catalog:<name>` whose catalog does not declare the package; `pnpm install` would fail. Default `error`. Suppress via `ignoreCatalogReferences: [{ package, catalog?, consumer? }]` in fallow config (package.json has no comment syntax). |
|
|
163
|
-
| `unused-dependency-override` | `--unused-dependency-overrides` | - | - | `pnpm-workspace.yaml#overrides
|
|
164
|
-
| `misconfigured-dependency-override` | `--misconfigured-dependency-overrides` | - | - |
|
|
163
|
+
| `unused-dependency-override` | `--unused-dependency-overrides` | - | - | Entries in `pnpm-workspace.yaml#overrides`, `package.json#pnpm.overrides`, npm or Bun `package.json#overrides`, or Bun `package.json#resolutions` whose target package is not declared by any workspace `package.json` and is not present in the active readable lockfile. Default `warn`. pnpm and npm projects without a readable lockfile degrade to a manifest-only fallback with a verification `hint`; Bun projects with only binary `bun.lockb` fail closed and emit no finding. Suppress via `ignoreDependencyOverrides: [{ package, source? }]` in fallow config. |
|
|
164
|
+
| `misconfigured-dependency-override` | `--misconfigured-dependency-overrides` | - | - | Package-manager override entries whose key is unparsable or whose value is missing or empty. The active package manager may reject or ignore the entry. Default `error`. Suppression: same `ignoreDependencyOverrides` config rule. |
|
|
165
165
|
| `invalid-client-export` | - | - | `// fallow-ignore-next-line invalid-client-export` | "use client" file exports a server-only / route-config name; Requires the project to declare next |
|
|
166
166
|
| `mixed-client-server-barrel` | - | - | `// fallow-ignore-next-line mixed-client-server-barrel` | Barrel re-exports both a "use client" module and a server-only module; Requires the project to declare next |
|
|
167
167
|
| `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 |
|
|
@@ -257,7 +257,7 @@ fallow list --entry-points --format json --quiet
|
|
|
257
257
|
fallow list --plugins --format json --quiet
|
|
258
258
|
```
|
|
259
259
|
|
|
260
|
-
Shows detected entry points and active framework plugins
|
|
260
|
+
Shows detected entry points and active framework plugins. Read `fallow schema.plugins.count` when the exact current registry size matters.
|
|
261
261
|
|
|
262
262
|
### Production-only analysis
|
|
263
263
|
```bash
|
|
@@ -428,11 +428,7 @@ fallow hooks install --target git # pre-commit gate; --branch <ref> sets the f
|
|
|
428
428
|
|
|
429
429
|
## Exit Codes
|
|
430
430
|
|
|
431
|
-
|
|
432
|
-
|------|---------|
|
|
433
|
-
| 0 | Success, no error-severity issues |
|
|
434
|
-
| 1 | Error-severity issues found |
|
|
435
|
-
| 2 | Runtime error (invalid config, parse failure, or `fix` without `--yes` in non-TTY) |
|
|
431
|
+
Codes 0 and 1 are successful analysis outcomes: 0 is clean and 1 means findings. Read `fallow schema.exit_codes` for validation, resource, runtime, network, security-gate, and upload failures instead of maintaining another copied table.
|
|
436
432
|
|
|
437
433
|
When `--format json` is active and exit code is 2, errors are emitted as JSON on stdout:
|
|
438
434
|
```json
|
|
@@ -441,7 +437,7 @@ When `--format json` is active and exit code is 2, errors are emitted as JSON on
|
|
|
441
437
|
|
|
442
438
|
## Configuration
|
|
443
439
|
|
|
444
|
-
Fallow reads config from project root: `.fallowrc.json` > `.fallowrc.jsonc` > `fallow.toml` > `.fallow.toml`. Both `.fallowrc.json` and `.fallowrc.jsonc` accept JSON-with-comments syntax (same parser); the `.jsonc` extension lets editors auto-detect JSONC syntax highlighting. Most projects work with zero configuration thanks to
|
|
440
|
+
Fallow reads config from project root: `.fallowrc.json` > `.fallowrc.jsonc` > `fallow.toml` > `.fallow.toml`. Both `.fallowrc.json` and `.fallowrc.jsonc` accept JSON-with-comments syntax (same parser); the `.jsonc` extension lets editors auto-detect JSONC syntax highlighting. Most projects work with zero configuration thanks to auto-detecting framework plugins; read `fallow schema.plugins` for the current registry.
|
|
445
441
|
|
|
446
442
|
```jsonc
|
|
447
443
|
{
|
|
@@ -478,7 +474,7 @@ export const deprecatedHelper = () => {};
|
|
|
478
474
|
## Key Gotchas
|
|
479
475
|
|
|
480
476
|
- **`fix --yes` is required** in non-TTY (agent) environments. Without it, `fix` exits with code 2
|
|
481
|
-
- **Zero config by default.**
|
|
477
|
+
- **Zero config by default.** Built-in framework plugins auto-detect, including Wuchale config, Contentlayer content roots, tap and tsd test entry points. Read `fallow schema.plugins` for the current registry and don't create config unless customization is needed
|
|
482
478
|
- **Syntactic analysis only.** No TypeScript compiler, so fully dynamic `import(variable)` is not resolved
|
|
483
479
|
- **Function overloads are deduplicated.** TypeScript function overload signatures are merged into a single export (not reported as separate unused exports)
|
|
484
480
|
- **Re-export chains are resolved.** Exports through barrel files are tracked, not falsely flagged
|
|
@@ -89,8 +89,8 @@ Common global flags for this command: [`--format`](#global-flags), [`--quiet`](#
|
|
|
89
89
|
| `--unused-catalog-entries` | Unused pnpm catalog entries |
|
|
90
90
|
| `--empty-catalog-groups` | Empty named pnpm catalog groups |
|
|
91
91
|
| `--unresolved-catalog-references` | Package references to missing pnpm catalog entries |
|
|
92
|
-
| `--unused-dependency-overrides` | Unused
|
|
93
|
-
| `--misconfigured-dependency-overrides` |
|
|
92
|
+
| `--unused-dependency-overrides` | Unused package-manager dependency overrides |
|
|
93
|
+
| `--misconfigured-dependency-overrides` | Misconfigured package-manager dependency overrides |
|
|
94
94
|
<!-- generated:flags:dead-code-filters:end -->
|
|
95
95
|
### Examples
|
|
96
96
|
|
|
@@ -298,7 +298,7 @@ fallow list --workspaces --format json --quiet
|
|
|
298
298
|
fallow workspaces --format json --quiet # alias of `fallow list --workspaces`
|
|
299
299
|
```
|
|
300
300
|
|
|
301
|
-
The `--workspaces` JSON output carries `workspaces[]` (name, project-root-relative path, `is_internal_dependency` bool) plus `workspace_diagnostics[]`. Each diagnostic has a `kind` discriminator (`undeclared-workspace`, `malformed-package-json`, `glob-matched-no-package-json`, `malformed-tsconfig`, `tsconfig-reference-dir-missing`) with a typed payload (`error`, `pattern`, or none). The same `workspace_diagnostics[]` array is also surfaced on `fallow dead-code --format json`, `fallow dupes --format json`, and `fallow health --format json` envelopes (omitted when empty). A malformed ROOT `package.json` exits 2 at config load; everything else warns and continues.
|
|
301
|
+
The `--workspaces` JSON output carries `workspaces[]` (name, project-root-relative path, `is_internal_dependency` bool) plus `workspace_diagnostics[]`. Each diagnostic has a `kind` discriminator (`undeclared-workspace`, `malformed-package-json`, `glob-matched-no-package-json`, `malformed-tsconfig`, `tsconfig-reference-dir-missing`, `malformed-pnpm-workspace-yaml`, `skipped-large-file`, `skipped-minified-file`, `source-read-failure`, `bun-lockb-override-resolution-skipped`) with a typed payload (`error`, `pattern`, or none), and a `path` that is project-root-relative with forward slashes on every envelope that carries the array. The same `workspace_diagnostics[]` array is also surfaced on the `fallow dead-code --format json`, `fallow dupes --format json`, and `fallow health --format json` envelopes, at the top level of the bare combined `fallow --format json` envelope, on `fallow audit --format json` under `dead_code`, and on the `audit-brief` envelope shared by `fallow review --format json` and `fallow audit --brief --format json`, also under `dead_code` (omitted when empty). The combined carrier is the envelope root, not a section, so `--skip check`, `--only health`, and `--only dupes` all still report what their analyses recorded. The combined root is the union of what every analysis in the run recorded, deduplicated on the whole `kind` (typed payload included) plus `path`, so two overlapping globs still report the same package-less directory once per `pattern` (a declared glob's no-op `./` prefix is normalised away, so one glob written `"./apps/**"` in `package.json` and `apps/**` in `pnpm-workspace.yaml` stays one entry): a combined run walks the project once per analysis, and a per-analysis `production` mode (`production: { deadCode, health, dupes }`, `--production-health`) can give those walks different file sets, so only the union reports what the run as a whole saw. Each analysis contributes the workspace-discovery list its own config load produced, the same list `fallow list --workspaces` reports, so the combined root can carry an `undeclared-workspace` or `glob-matched-no-package-json` entry that the standalone `dead-code`, `check`, `health`, and `dupes` envelopes, which read the process diagnostics registry instead, do not. `fallow audit --format json` and the `audit-brief` envelope are on the same broad side: they fold the dead-code analysis's own list into their `dead_code.workspace_diagnostics[]`, so they too report an `undeclared-workspace` entry the standalone envelopes miss. The CLI and the programmatic route (MCP code mode, NAPI, embedders) agree on everything an analysis records: both folds close with the same process-registry read, which covers what an analysis records after its section captured its list (a `source-read-failure`, or the analysis-stage kinds a health run's own dead-code precompute records) and skips `skipped-large-file` and `skipped-minified-file`, since those reach an envelope only from the walk that recorded them. The two analysis-stage kinds (`malformed-pnpm-workspace-yaml`, `bun-lockb-override-resolution-skipped`) are recorded by the dead-code analyze pass, so they only appear on runs that include it: `fallow dupes --format json` and `fallow --only dupes` report the workspace-discovery and source-discovery kinds alone. A malformed ROOT `package.json` exits 2 at config load; everything else warns and continues.
|
|
302
302
|
|
|
303
303
|
The `--boundaries` JSON output carries `boundaries.logical_groups[]` alongside the existing `zones[]` / `rules[]` arrays. Each logical-group entry surfaces a user-authored `autoDiscover` parent zone (which expansion otherwise flattens into per-child zones like `features/auth` / `features/billing`): `name`, `children`, `auto_discover` (verbatim user strings), `status` (`ok` / `empty` / `invalid_path`), `source_zone_index`, summed `file_count`, optional `authored_rule` (the pre-expansion `{ allow, allowTypeOnly }` keyed on the parent), optional `fallback_zone` cross-reference when the parent also kept its own `patterns` (Bulletproof case), optional `merged_from` (parent zone indices when the user declared the same parent name twice; surfaces the duplicate in JSON instead of only in `tracing::warn!`), optional `original_zone_root` (echo of the parent's `root` subtree scope for monorepo patchers), and optional `child_source_indices` (parallel to `children`, attributing each child to a specific `auto_discover` entry when multiple paths were authored). The full shape is documented in `docs/output-schema.json` under `ListBoundariesOutput`.
|
|
304
304
|
|
|
@@ -838,8 +838,8 @@ Audits changed files for dead code, complexity, duplication, and styling. Return
|
|
|
838
838
|
| `--health-baseline` | `string` | - | Baseline file (produced by `fallow health --save-baseline`). Pre-existing complexity findings are excluded from the verdict. |
|
|
839
839
|
| `--dupes-baseline` | `string` | - | Baseline file (produced by `fallow dupes --save-baseline`). Pre-existing clone groups are excluded from the verdict. |
|
|
840
840
|
| `--max-crap` | `string` | - | Forwarded to the health sub-analysis. Functions meeting or exceeding this CRAP score cause audit to fail. Same formula as `health --max-crap`. Pair with coverage data for accurate per-function CRAP. |
|
|
841
|
-
| `--coverage` | `string` | - | Path to Istanbul-format coverage data (`coverage-final.json`) for accurate per-function CRAP scores in the health sub-analysis. Same format and semantics as `health --coverage`. Also configurable via `FALLOW_COVERAGE
|
|
842
|
-
| `--coverage-root` | `string` | - | Absolute prefix to strip from file paths in coverage data before prepending the project root. Also configurable via `FALLOW_COVERAGE_ROOT`. Use when coverage was generated under a different checkout root in CI / Docker (e.g., `/home/runner/work/myapp` on GitHub Actions). |
|
|
841
|
+
| `--coverage` | `string` | - | Path to Istanbul-format coverage data (`coverage-final.json`) for accurate per-function CRAP scores in the health sub-analysis. Same format and semantics as `health --coverage`. Also configurable via `FALLOW_COVERAGE`, then `health.coverage` (the same chain as `fallow health`). Relative paths resolve against `--root`. |
|
|
842
|
+
| `--coverage-root` | `string` | - | Absolute prefix to strip from file paths in coverage data before prepending the project root. Also configurable via `FALLOW_COVERAGE_ROOT`, then `health.coverageRoot`. Use when coverage was generated under a different checkout root in CI / Docker (e.g., `/home/runner/work/myapp` on GitHub Actions). |
|
|
843
843
|
| `--no-css` | `bool` | `false` | Disable styling analytics in audit |
|
|
844
844
|
| `--css-deep` | `bool` | `false` | Enable deep CSS analysis for audit explicitly: project-wide styling reachability, narrowed back to changed anchors. Deep CSS is on by default; use this to override `audit.cssDeep = false` |
|
|
845
845
|
| `--no-css-deep` | `bool` | `false` | Disable deep CSS analysis while keeping local styling analytics on |
|
|
@@ -23,7 +23,7 @@ Always preview with `--dry-run` before applying. This is a destructive operation
|
|
|
23
23
|
|
|
24
24
|
## Don't Create Config Unless Needed
|
|
25
25
|
|
|
26
|
-
Fallow works with zero configuration for most projects thanks to
|
|
26
|
+
Fallow works with zero configuration for most projects thanks to auto-detecting framework plugins. Read `fallow schema.plugins` for the current registry. Creating an unnecessary config file can mask issues or override detection behavior.
|
|
27
27
|
|
|
28
28
|
```bash
|
|
29
29
|
# WRONG: creating config for a standard Next.js project
|
|
@@ -38,7 +38,7 @@ When using fallow via MCP (`fallow-mcp`), the following tools are available:
|
|
|
38
38
|
| `impact` | introspection | free | `fallow impact --format json --quiet` | `root` | Read the local, opt-in Fallow Impact value report (`fallow impact --format json`). Runs no analysis: current surfacing counts, trend since the last recorded run, pre-commit gate containment, and (on impact v1.5+) resolved/suppressed attribution. History is read from a per-project file in the user's private config dir (never inside the repo). Read-only and `root`-only; the mutating `enable` / `disable` / `default` lifecycle is not exposed. A never-enabled project returns a populated `{"enabled": false, ...}` report (never `{}`); branch on `enabled` and `enabled_source` (`project` / `user` / `default`) then `record_count`, recommending `fallow impact enable` only when `explicit_decision` is `false` (never asked) and staying silent when `true` (deliberately disabled here). Local-developer signal: fallow never records in CI, so empty there and not a CI metric |
|
|
39
39
|
| `impact_all` | introspection | free | `fallow impact --all --format json --quiet` | `sort`, `limit` | Roll every tracked fallow project on this machine into one cross-repo value report (hashed keys plus basename labels, never paths; local-dev only) |
|
|
40
40
|
| `trace_export` | trace | free | `fallow dead-code --trace <file:export> --format json --quiet` | `file`, `export_name` | Trace why an export is used or unused (`fallow dead-code --trace FILE:EXPORT_NAME --format json`). Required `file` and `export_name`. Returns file reachability, entry-point status, direct references, re-export chains, and a reason string. If `export_name` is a class / enum / store MEMBER, returns a member trace instead (`member_name`, `member_kind`, `owner_export`, `owner_is_used`) plus a `--unused-<kind>-members` pointer; branch on field presence. Use before deleting a supposedly-unused export or debugging an unused-class-member finding |
|
|
41
|
-
| `trace_symbol` | trace | free | `fallow dead-code --type-aware --trace <file:export> --format json --quiet` | `file`, `export_name`, `type_aware_projects`, `type_aware_require` | Trace an exact TypeScript symbol with checker-backed references, namespace identity, aliases, and re-export hops. Root trace fields preserve syntactic context; treat `semantic.references`, `semantic.status`, and `semantic.identity` as the authoritative exact evidence. This is project-wide evidence for Fallow decisions, not a compiler-diagnostic or lint-rule surface. |
|
|
41
|
+
| `trace_symbol` | trace | free | `fallow dead-code --type-aware --trace <file:export> --format json --quiet` | `file`, `export_name`, `type_aware_projects`, `type_aware_require` | Trace an exact TypeScript symbol with checker-backed references, namespace identity, aliases, and re-export hops. Root trace fields preserve syntactic context; treat `semantic.references`, `semantic.status`, and `semantic.identity` as the authoritative exact evidence. The proof covers only the lane named by `semantic.target.namespace`, so a root trace that lists a reference the proof does not is wider evidence rather than stale. This is project-wide evidence for Fallow decisions, not a compiler-diagnostic or lint-rule surface. |
|
|
42
42
|
| `symbol_impact` | impact | free | `fallow dead-code --type-aware --symbol-impact <file:export-or-class.member> --format json --quiet` | `file`, `export_name`, `class_name`, `member_name`, `type_aware_projects`, `type_aware_require` | Return exact-symbol consumers, affected files, and targeted tests for a TypeScript export or exported class method. Select either `export_name`, or both `class_name` and `member_name`. Advisory change-impact evidence, not a substitute for `tsc` or Oxlint |
|
|
43
43
|
| `trace_file` | trace | free | `fallow dead-code --trace-file <file> --format json --quiet` | `file` | Trace all graph edges for a file (`fallow dead-code --trace-file PATH --format json`). Required `file`. Returns reachability, exports, imports-from, imported-by, and re-exports. Use to decide whether a file is isolated, barrel-only, or imported by live entry points |
|
|
44
44
|
| `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 |
|
|
@@ -46,6 +46,29 @@ When using fallow via MCP (`fallow-mcp`), the following tools are available:
|
|
|
46
46
|
| `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 |
|
|
47
47
|
<!-- generated:mcp-tools:end -->
|
|
48
48
|
|
|
49
|
+
## How type-aware proof relates to the root trace
|
|
50
|
+
|
|
51
|
+
`trace_symbol` is the only tool that returns a checker-backed `semantic` block
|
|
52
|
+
next to a syntactic root trace. The checker resolves actual reads through local
|
|
53
|
+
aliases, import types, namespace-qualified names, and barrels to the exact type
|
|
54
|
+
or value declaration. Import and re-export declarations alone are not reads.
|
|
55
|
+
The root trace stays authoritative for graph reachability and star ambiguity,
|
|
56
|
+
and its optional `direct_references_by_namespace` keeps type and value evidence
|
|
57
|
+
separate without changing the selected root `namespace`. Type-aware
|
|
58
|
+
reconciliation fails closed: unreachable-only, re-export-only, or
|
|
59
|
+
different-declaration evidence cannot suppress a syntactic finding. Treat an
|
|
60
|
+
ambiguous root as an abstention, and investigate any remaining mismatch before
|
|
61
|
+
deleting a symbol.
|
|
62
|
+
|
|
63
|
+
`symbol_impact` carries no `semantic` block and no root trace. Its top-level
|
|
64
|
+
checker evidence uses the same declaration-safe alias and namespace resolution
|
|
65
|
+
as `trace_symbol`. A listed consumer's `relation` names the traced symbol's own
|
|
66
|
+
lane, not the consumer's syntax. Confirm a clean impact result with
|
|
67
|
+
`trace_export` or `trace_symbol` before deletion when the graph reports
|
|
68
|
+
ambiguity or reachable references.
|
|
69
|
+
|
|
70
|
+
`trace_export` never carries a `semantic` block: it is API-backed in-process and answers from the graph alone.
|
|
71
|
+
|
|
49
72
|
## Runtime source-map confidence for cloud runtime tools
|
|
50
73
|
|
|
51
74
|
| Values | Meaning | Agent action |
|
|
@@ -645,7 +645,7 @@ Focus on findings that are BOTH dead code and duplicated:
|
|
|
645
645
|
|
|
646
646
|
## Custom Plugin Setup
|
|
647
647
|
|
|
648
|
-
For frameworks not covered by the
|
|
648
|
+
For frameworks not covered by the current built-in registry from `fallow schema.plugins`.
|
|
649
649
|
|
|
650
650
|
### Option 1: Inline framework config
|
|
651
651
|
|
|
@@ -717,7 +717,7 @@ fallow dead-code --format sarif --quiet > fallow.sarif
|
|
|
717
717
|
fallow dead-code --ci > fallow.sarif
|
|
718
718
|
```
|
|
719
719
|
|
|
720
|
-
The `--ci` flag is equivalent to `--format sarif --fail-on-issues --quiet`.
|
|
720
|
+
The `--ci` flag is equivalent to `--format sarif --fail-on-issues --quiet`. Exit code 1 means findings exist. Capture that status, let the SARIF upload step run, then reapply the captured status in a final gate step. Do not discard every outcome, because validation and execution failures need to remain distinguishable from findings.
|
|
721
721
|
|
|
722
722
|
---
|
|
723
723
|
|