fallow 3.31.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 +17 -10
- package/capabilities.json +267 -157
- package/issue-registry.json +190 -154
- package/package.json +12 -12
- package/schema.json +31 -1
- package/skills/fallow/SKILL.md +29 -9
- package/skills/fallow/references/cli-reference.md +83 -38
- package/skills/fallow/references/gotchas.md +21 -13
- package/skills/fallow/references/issue-types.md +3 -2
- package/skills/fallow/references/mcp.md +17 -5
- 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 +352 -24
|
@@ -14,7 +14,7 @@ When using fallow via MCP (`fallow-mcp`), the following tools are available:
|
|
|
14
14
|
| `code_execute` | composition | free | - | `code`, `timeout_ms`, `max_output_bytes` | Bounded read-only Code Mode for composing multiple fallow analysis calls in one JavaScript snippet. The snippet receives `{ fallow, root }`, returns JSON-serializable data, and can call read-only helpers such as `fallow.projectInfo`, `fallow.audit`, `fallow.checkHealth`, and `fallow.run(tool, params)` for the same allowlist. `fallow.all(requests)` fans out independent calls in one go: pass `[{ tool, params }, ...]` and get back a positionally aligned array of `{ ok: true, value }` or `{ ok: false, error }`, so one failing element never hides the rest. Host calls are memoized for the duration of one snippet, so repeating the same tool with the same params (key order does not matter) is served from cache, spends no `max_host_calls` slot and no output budget, and is reported in `calls[]` with `cache_hit: true`; a call refused before dispatch (unknown tool, malformed params) spends no slot either, and `limits.max_rejected_host_calls` bounds how many of those the response records. Similar-code is excluded because Code Mode is capped at 30 seconds; use standalone `find_similar_code` and `inspect_similar_code`, which have dedicated 15-minute timeouts. Mutating fix tools are not exposed, and a host call that passes `save_baseline`, `save_regression_baseline` or `save_snapshot` is refused with the name of the standalone tool to call. The sandbox has no filesystem, network, imports, `process`, `require`, `Deno`, `Bun`, or shell access, and no dynamic code compilation: `eval`, `Function`, and the async and generator function constructors are removed, including the `constructor` route reachable through function prototypes. Params: `code`, optional `root`, `timeout_ms` (capped at 30000), and `max_output_bytes` (capped at 4000000). `max_output_bytes` bounds two separate things: the total fallow JSON host calls read, shared across a `fallow.all` fan-out rather than granted per element, and the serialized snippet result. An oversized result is refused with `ok:false`, `truncated:true`, `result_bytes`, and a short `result_preview` in place of the value, never returned whole, so return a projection rather than a whole report. |
|
|
15
15
|
| `analyze` | analysis | free | `fallow dead-code --format json --quiet` | `issue_types`, `production`, `workspace`, `baseline`, `group_by`, `file` | Full dead code analysis (unused files/exports/types/dependencies/members + circular dependencies + re-export cycles (barrel files that form a structural loop, silently breaking re-exports) + package cycles (workspace packages that import each other in a loop) + boundary violations + rule-pack policy violations (banned calls, imports, and catalogue-derived effects declared via the `rulePacks` config key) + stale suppressions). Private type leaks are an opt-in API hygiene check via `issue_types: ["private-type-leaks"]`. Deprecated exports that still have consumers are an opt-in migration sweep via `issue_types: ["deprecated-exports-in-use"]`. Set `boundary_violations: true` as a convenience alias for `issue_types: ["boundary-violations"]`. Set `group_by` to `"owner"`, `"directory"`, `"package"`, or `"section"` to partition results. The `section` mode reads GitLab CODEOWNERS `[Section]` headers and emits `owners` metadata per group |
|
|
16
16
|
| `check_changed` | analysis | free | `fallow dead-code --changed-since <ref> --format json --quiet` | `since`, `baseline`, `fail_on_regression` | Incremental analysis of files changed since a git ref |
|
|
17
|
-
| `security_candidates` | analysis | free | `fallow security --format json --quiet` | `gate`, `surface`, `changed_since`, `paths` | Unverified local security candidates, not confirmed vulnerabilities (`fallow security --format json`). Read `security_findings[]` for category, CWE, severity, evidence, trace, optional `reachability`, blind-spot counters, and optional `unresolved_callee_diagnostics` samples for dynamic callee follow-up. `severity` is a review-priority tier, not a verified vulnerability verdict. Each finding also carries an agent-actionable `candidate` (`source_kind`/`sink`/`boundary`), where URL-category sinks may include `url_shape` (`fixed-origin-dynamic-path` or `dynamic-origin`), an optional `taint_flow` source-to-sink triple, and a stable `finding_id` (equal to the SARIF fingerprint) for cross-run correlation; there is no `impact` field (deciding exploitability is the agent's job). Set `surface: true` to include top-level `attack_surface[]` entries with defensive-boundary prompts for a verifier. Set `gate` to `new` for changed-line candidates or `newly-reachable` for candidates that became reachable from entry points; `newly-reachable` requires `changed_since`. `reachability.untrusted_source_trace` is module-level import context only and does not prove value flow; `reachability.taint_confidence` tiers each reachable candidate as `arg-level` (sink argument traces to a same-module source read, strong) or `module-level` (only the module is import-reachable from a source, weak), so tier from this field instead of the evidence text. Verify trace, reachability context, severity, and evidence before editing code. Supports `root`, `config`, `workspace`, `paths`, `changed_since`, `changed_workspaces`, `surface`, `gate`, `no_cache`, and `threads`; `paths` forwards repeated `fallow security --file` filters for finding anchors, trace hops, untrusted-source reachability trace hops, and unresolved-callee diagnostics. See <https://
|
|
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. |
|
|
@@ -50,14 +50,20 @@ When using fallow via MCP (`fallow-mcp`), the following tools are available:
|
|
|
50
50
|
| `trace_error` | trace | free | `fallow trace-error - --format json --quiet` | `trace` | Resolve a runtime stack trace's frames against the project graph |
|
|
51
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 |
|
|
52
52
|
| `trace_dependency` | trace | free | `fallow dead-code --trace-dependency <package> --format json --quiet` | `package_name` | Trace where a dependency is imported (`fallow dead-code --trace-dependency PACKAGE --format json`). Required `package_name`. Returns importing files, type-only importers, total import count, `used_in_scripts` (true when invoked from package.json scripts or CI configs), and `is_used` (combined import + script signal; mirrors the unused-deps detector so build tools like `microbundle` or `vitest` are not falsely flagged as unused). Use before removing a dependency or moving between `dependencies` and `devDependencies` |
|
|
53
|
-
| `trace_clone` | trace | free | `fallow dupes --trace <file:line> --format json --quiet` | `file`, `line`, `fingerprint`, `near`, `min_occurrences` | Deep-dive a duplicate-code clone group (`fallow dupes --trace <spec> --format json`). Address by exactly one of: `file` + `line` (a source location), or `fingerprint` (a `dup:<id>` from a prior `find_dupes` `clone_groups[].fingerprint`, usually `dup:<8hex>` and widened only on rare report collisions). Returns the matched clone instance plus every clone group containing it; each traced group carries its `fingerprint`, an extract-function `suggestion` with estimated savings, and a best-effort `suggested_name` (omitted when no confident name). Supports `mode`, `near`, `min_tokens`, `min_lines`, `min_occurrences`, `threshold`, `skip_local`, `cross_language`, `ignore_imports`. Use the same `near` value as the originating `find_dupes` call. Use to consolidate duplication when you need exact sibling locations and a refactor target |
|
|
53
|
+
| `trace_clone` | trace | free | `fallow dupes --trace <file:line> --format json --quiet` | `file`, `line`, `fingerprint`, `near`, `min_occurrences` | Deep-dive a duplicate-code clone group (`fallow dupes --trace <spec> --format json`). Address by exactly one of: `file` + `line` (a source location), or `fingerprint` (a `dup:<id>` from a prior `find_dupes` `clone_groups[].fingerprint`, usually `dup:<8hex>` and widened only on rare report collisions). Returns the matched clone instance plus every clone group containing it; each traced group carries its `fingerprint`, an extract-function `suggestion` with estimated savings, and a best-effort `suggested_name` (omitted when no confident name). Supports `mode`, `near`, `min_tokens`, `min_lines`, `min_occurrences`, `threshold`, `skip_local`, `ignore_symlinks`, `cross_language`, `ignore_imports`. Use the same `near` value as the originating `find_dupes` call. Use to consolidate duplication when you need exact sibling locations and a refactor target |
|
|
54
54
|
<!-- generated:mcp-tools:end -->
|
|
55
55
|
|
|
56
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`.
|
|
57
57
|
|
|
58
58
|
## Resource catalogue
|
|
59
59
|
|
|
60
|
-
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`).
|
|
61
67
|
|
|
62
68
|
<!-- generated:mcp-resources:start -->
|
|
63
69
|
| Resource | Name | Kind | MIME | Description |
|
|
@@ -138,8 +144,14 @@ An entry in `not_found` has no cloud data. Absence is not evidence that the code
|
|
|
138
144
|
|
|
139
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.
|
|
140
146
|
|
|
141
|
-
|
|
147
|
+
Every dead-code, health, and duplication finding in JSON responses includes a structured `actions` array for programmatic fixes or suppression.
|
|
142
148
|
|
|
143
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.
|
|
144
150
|
|
|
145
|
-
`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.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Configure and install
|
|
2
|
+
|
|
3
|
+
## Recommendation
|
|
4
|
+
|
|
5
|
+
`fallow recommend --format json --quiet` inspects the project and changes nothing. It always exits 0. The envelope has `kind: "recommendation"` and these fields:
|
|
6
|
+
|
|
7
|
+
| Field | Content |
|
|
8
|
+
|---|---|
|
|
9
|
+
| `detected` | The project facts: monorepo layout, TypeScript, test framework, UI framework, Storybook, package manager |
|
|
10
|
+
| `proposed_config` | A safe starting config |
|
|
11
|
+
| `decisions[]` | One entry per setting: `setting`, `value`, `rationale`, `kind`, `question` |
|
|
12
|
+
| `config_schema_command` | The command that prints the config JSON Schema |
|
|
13
|
+
|
|
14
|
+
Use `kind` to decide what to do with each decision:
|
|
15
|
+
|
|
16
|
+
| `kind` | Meaning | Action |
|
|
17
|
+
|---|---|---|
|
|
18
|
+
| `auto` | Fallow decided the value from the detection | Apply it. |
|
|
19
|
+
| `default` | A disclosed default that the user can change | Apply it and tell the user the `rationale` in one line. |
|
|
20
|
+
| `taste` | A subjective choice | Keep the current value, or ask the user. `question` has a `header`, a `question`, and `options[]` with a `label` and a `description`. Show these options as they are. |
|
|
21
|
+
|
|
22
|
+
When the project has no Fallow config, write `proposed_config` plus the answers to `.fallowrc.json`. When the project has a config, change only the settings that the user approved. Validate the keys with `fallow config-schema`. Check the loaded config with `fallow config --format json --quiet`.
|
|
23
|
+
|
|
24
|
+
When the project has a Knip, jscpd, or stylelint config, preview the migration with `fallow migrate --dry-run`. Merge the result with the recommendation. Do not delete the old config in this step. The [parity check](tooling-detection.md#parity-check) decides when it can go.
|
|
25
|
+
|
|
26
|
+
## Dev dependency
|
|
27
|
+
|
|
28
|
+
Install Fallow as a dev dependency with the detected package manager:
|
|
29
|
+
|
|
30
|
+
| Package manager | Command |
|
|
31
|
+
|---|---|
|
|
32
|
+
| npm | `npm install --save-dev fallow` |
|
|
33
|
+
| pnpm | `pnpm add -D fallow` |
|
|
34
|
+
| Yarn | `yarn add -D fallow` |
|
|
35
|
+
| Bun | `bun add -d fallow` |
|
|
36
|
+
|
|
37
|
+
A dev dependency pins one Fallow version for every developer, every agent, and CI. Add scripts to `package.json` when the project uses scripts for its other checks, for example `"fallow": "fallow"` and `"fallow:audit": "fallow audit"`.
|
|
38
|
+
|
|
39
|
+
## Agent wiring
|
|
40
|
+
|
|
41
|
+
`fallow agent install` wires Fallow into Claude Code, Codex, and Cursor in one pass. It detects the harnesses from the project, the home directory, and the session environment. `--harness` selects them explicitly.
|
|
42
|
+
|
|
43
|
+
1. Run `fallow agent install --dry-run --format json --quiet`. Each entry in `steps[]` has a `step`, a `status`, and a `path`. Show the plan to the user.
|
|
44
|
+
2. Run `fallow agent install`. Add `--without <step>` to skip a step. The steps are `guide`, `skill`, `mcp`, and `hooks`.
|
|
45
|
+
3. Run `fallow agent status --format json --quiet`. Check only the entries of the harnesses that step 1 selected. Status also lists the other harnesses (for example `.cursor/mcp.json` in a Claude-only project) as `absent`, which is correct. Act on `next_actions[]` for a `stale` entry of a selected harness.
|
|
46
|
+
|
|
47
|
+
The steps write these items:
|
|
48
|
+
|
|
49
|
+
| Step | Result |
|
|
50
|
+
|---|---|
|
|
51
|
+
| `guide` | The task map in `AGENTS.md`, and an `@AGENTS.md` import in `CLAUDE.md` for Claude Code |
|
|
52
|
+
| `skill` | The Fallow skills under `.claude/skills/` and `.agents/skills/` |
|
|
53
|
+
| `mcp` | The MCP server registration for each harness |
|
|
54
|
+
| `hooks` | The commit and push gate: a PreToolUse hook for Claude Code (`.claude/settings.json`) and for Codex (`.codex/hooks.json`), plus a routing block in `AGENTS.md` |
|
|
55
|
+
|
|
56
|
+
Exit code 2 means that a step is `refused` or `failed`. Read the `reason` of that step. `skill_name_taken` means that a skill with the same name exists and Fallow did not write it. Do not pass `--force` without the approval of the user.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Tooling detection
|
|
2
|
+
|
|
3
|
+
Inspect the repository before you change it. Record what you find, because the later steps use it.
|
|
4
|
+
|
|
5
|
+
## Files to inspect
|
|
6
|
+
|
|
7
|
+
| Tool area | Signals |
|
|
8
|
+
|---|---|
|
|
9
|
+
| Package manager | `packageManager` in `package.json`; the lockfile: `pnpm-lock.yaml`, `yarn.lock`, `bun.lock` or `bun.lockb`, `package-lock.json` |
|
|
10
|
+
| Formatter | `.oxfmtrc.json`, `.prettierrc*`, `prettier.config.*`, `biome.json`, `biome.jsonc`, a `format` script in `package.json` |
|
|
11
|
+
| Linter | `.oxlintrc.json`, `eslint.config.*`, `.eslintrc*`, `biome.json`, a `lint` script in `package.json` |
|
|
12
|
+
| TypeScript | `tsconfig.json`, `tsconfig.*.json`, a `typecheck` script, `typescript` in `devDependencies` |
|
|
13
|
+
| CI | `.github/workflows/*.yml`, `.gitlab-ci.yml`, other CI config files |
|
|
14
|
+
| Existing analysis | `knip.json`, `knip.jsonc`, `.knip.json`, `.knip.jsonc`, a `knip` field in `package.json`; `.jscpd.json`; `.dependency-cruiser.*` |
|
|
15
|
+
| Existing Fallow | `fallow config --path` prints the config path, or exits 3 when there is no config |
|
|
16
|
+
| Agent harnesses | `CLAUDE.md`, `.claude/`, `.codex/`, `.cursor/` (`AGENTS.md` alone does not name a harness) |
|
|
17
|
+
|
|
18
|
+
`fallow doctor --format json --quiet` checks the project root, the config, the workspaces, and the installed dependencies. It changes nothing. Run it when the project layout is not clear.
|
|
19
|
+
|
|
20
|
+
## Responsibility split
|
|
21
|
+
|
|
22
|
+
Give each tool one job. Do not configure two tools for the same job.
|
|
23
|
+
|
|
24
|
+
| Job | Tool | Command |
|
|
25
|
+
|---|---|---|
|
|
26
|
+
| Formatting | Oxfmt | `oxfmt` |
|
|
27
|
+
| Local lint rules | Oxlint | `oxlint` |
|
|
28
|
+
| Type correctness | TypeScript | `tsc --noEmit` |
|
|
29
|
+
| Repository and system analysis | Fallow | `fallow`, `fallow audit` |
|
|
30
|
+
|
|
31
|
+
Repository and system analysis covers unused files, exports, types, and dependencies, duplication, complexity, circular dependencies, architecture boundaries, and changed-code risk.
|
|
32
|
+
|
|
33
|
+
For a new project, use the tools in this table. For an existing project, keep the current formatter, linter, and type check. Add Fallow for the system analysis.
|
|
34
|
+
|
|
35
|
+
## Parity check
|
|
36
|
+
|
|
37
|
+
Do this check before you remove a tool whose job overlaps with Fallow, for example Knip, jscpd, or dependency-cruiser. When a step fails, keep the tool and tell the user why.
|
|
38
|
+
|
|
39
|
+
1. Ask the user for approval to replace the tool.
|
|
40
|
+
2. Migrate the config when Fallow can read it: `fallow migrate --dry-run` for Knip, jscpd, and stylelint. When the project has no Fallow config, review the preview, then run `fallow migrate`. When a Fallow config exists, `fallow migrate` refuses to write. Merge the settings from the preview into that config by hand. Fallow cannot migrate a dependency-cruiser config. Write the rules again as `boundaries` in the Fallow config (`fallow config-schema` gives the format).
|
|
41
|
+
3. Run the old tool and Fallow on the same commit. Compare the findings by category.
|
|
42
|
+
4. Explain each finding that only one tool reports. A finding that Fallow does not report must have a reason, for example a framework entry point that Fallow detects.
|
|
43
|
+
5. Move each CI step and each `package.json` script of the old tool to Fallow.
|
|
44
|
+
6. Remove the old tool and its config in a separate commit, so that the user can revert it.
|