fallow 2.69.0 → 2.71.1

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.
@@ -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 90 auto-detecting framework plugins. Creating an unnecessary config file can mask issues or override detection behavior.
26
+ Fallow works with zero configuration for most projects thanks to 94 auto-detecting framework plugins. 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
@@ -429,6 +429,26 @@ Fallow marks `static/style.css` and `static/app.js` as reachable. Root-relative
429
429
 
430
430
  ---
431
431
 
432
+ ## GraphQL `#import` Documents Are Tracked
433
+
434
+ GraphQL `.graphql` and `.gql` files can keep nearby fragment documents reachable with relative `#import` comments. Fallow tracks `./` and `../` specifiers, including extensionless imports that resolve through `.graphql` and `.gql`; package-style specifiers are ignored.
435
+
436
+ ```graphql
437
+ # src/query.graphql
438
+
439
+ #import "./fragments/user-fields"
440
+
441
+ query CurrentUser {
442
+ currentUser {
443
+ ...UserFields
444
+ }
445
+ }
446
+ ```
447
+
448
+ Fallow marks `src/fragments/user-fields.graphql` or `src/fragments/user-fields.gql` as reachable when either file exists. A typo in the relative path is reported as an unresolved import instead of silently dropping the edge.
449
+
450
+ ---
451
+
432
452
  ## Library Packages: Use `publicPackages` Instead of Visibility Tags
433
453
 
434
454
  In monorepos, shared library packages have exported APIs consumed by external consumers not visible to fallow. Instead of annotating every export with `/** @public */` (or `@internal`, `@beta`, `@alpha`), use the `publicPackages` config to mark entire workspace packages as public libraries. Exports and exported enum/class members from these packages are excluded from unused API detection.
@@ -129,6 +129,31 @@ Parse the JSON to list specific files and exports that became unused.
129
129
 
130
130
  Computes a health score (0-100 with letter grade) in combined mode and enables the health delta header in PR comments.
131
131
 
132
+ ### GitHub Actions: Severity-Aware PR Quality Gate (Audit)
133
+
134
+ ```yaml
135
+ - uses: fallow-rs/fallow@v2
136
+ with:
137
+ command: audit
138
+ gate: new-only # default; fails only on findings introduced by this PR
139
+ fail-on-issues: true
140
+ ```
141
+
142
+ Runs `fallow audit` to combine dead-code + complexity + duplication scoped to changed files. The gate respects rule severity from `.fallowrc.json`, so `unused-exports: warn` projects do not fail when a PR touches a file with pre-existing warn-tier findings. Use `gate: all` to fail on every finding in changed files.
143
+
144
+ The action exposes `outputs.verdict` (`pass`/`warn`/`fail`) and `outputs.gate` for downstream conditionals; `outputs.issues` holds the introduced count under `gate: new-only` and the total count under `gate: all`.
145
+
146
+ ```yaml
147
+ - uses: fallow-rs/fallow@v2
148
+ id: fallow
149
+ with:
150
+ command: audit
151
+
152
+ - name: Block release on regression
153
+ if: steps.fallow.outputs.verdict == 'fail'
154
+ run: exit 1
155
+ ```
156
+
132
157
  ### GitHub Actions: Inline PR Annotations (No Advanced Security)
133
158
 
134
159
  The official action supports inline PR annotations via GitHub workflow commands. This does not require Advanced Security (unlike SARIF upload) and works on any GitHub plan.
@@ -398,6 +423,7 @@ Creates `.fallowrc.json` with mapped settings:
398
423
  - knip `rules`/`exclude`/`include` → fallow `rules` (error/warn/off)
399
424
  - knip `ignore` → fallow `ignorePatterns`
400
425
  - knip `ignoreDependencies` → fallow `ignoreDependencies`
426
+ - 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)
401
427
  - Unmappable fields generate warnings with suggestions
402
428
 
403
429
  ### Step 3: Compare results
@@ -595,7 +621,7 @@ Focus on findings that are BOTH dead code and duplicated:
595
621
 
596
622
  ## Custom Plugin Setup
597
623
 
598
- For frameworks not covered by the 90 built-in plugins.
624
+ For frameworks not covered by the 94 built-in plugins.
599
625
 
600
626
  ### Option 1: Inline framework config
601
627
 
@@ -693,13 +719,13 @@ Because Claude receives stderr as tool feedback on a blocked `PreToolUse` call,
693
719
  Install it automatically:
694
720
 
695
721
  ```bash
696
- fallow setup-hooks
722
+ fallow hooks install --target agent
697
723
  ```
698
724
 
699
725
  Remove it later with:
700
726
 
701
727
  ```bash
702
- fallow setup-hooks --uninstall
728
+ fallow hooks uninstall --target agent
703
729
  ```
704
730
 
705
731
  Manual files:
@@ -727,79 +753,14 @@ Manual files:
727
753
 
728
754
  ### `.claude/hooks/fallow-gate.sh`
729
755
 
730
- ```bash
731
- #!/usr/bin/env bash
732
- set -euo pipefail
733
-
734
- # Generated by fallow setup-hooks.
735
- # Requires bash and jq. On Windows run via git-bash or WSL.
736
- # Blocks Claude Code git commit and git push when fallow audit returns verdict fail.
737
- # Runtime errors fail open with a single stderr notice so skips stay visible.
738
-
739
- if ! command -v jq >/dev/null 2>&1; then
740
- echo "fallow-gate: jq not on PATH, skipping audit." >&2
741
- exit 0
742
- fi
756
+ 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).
743
757
 
744
- INPUT="$(cat)"
745
- CMD="$(jq -r '.tool_input.command // empty' <<<"$INPUT")"
746
-
747
- if ! printf '%s\n' "$CMD" | grep -Eq '(^|[[:space:];|&()])git[[:space:]]+(commit|push)([[:space:]]|$)'; then
748
- exit 0
749
- fi
750
-
751
- if command -v fallow >/dev/null 2>&1; then
752
- RUNNER=(fallow)
753
- elif command -v npx >/dev/null 2>&1 && VER="$(npx --no-install fallow --version 2>/dev/null || true)" && [[ "$VER" == fallow* ]]; then
754
- RUNNER=(npx --no-install fallow)
755
- else
756
- echo "fallow-gate: fallow binary not found (tried PATH and npx --no-install), skipping audit." >&2
757
- exit 0
758
- fi
759
-
760
- TMP_JSON="$(mktemp)"
761
- TMP_ERR="$(mktemp)"
762
- cleanup() {
763
- rm -f "$TMP_JSON" "$TMP_ERR"
764
- }
765
- trap cleanup EXIT
766
-
767
- if "${RUNNER[@]}" audit --format json --quiet --explain >"$TMP_JSON" 2>"$TMP_ERR"; then
768
- STATUS=0
769
- else
770
- STATUS=$?
771
- fi
772
-
773
- VERDICT="$(jq -r '.verdict // empty' <"$TMP_JSON" 2>/dev/null || true)"
774
- IS_ERROR="$(jq -r '.error // false' <"$TMP_JSON" 2>/dev/null || echo false)"
775
-
776
- if [ "$VERDICT" = "fail" ]; then
777
- cat "$TMP_JSON" >&2
778
- exit 2
779
- fi
780
-
781
- if [ "$STATUS" -eq 2 ] || [ "$IS_ERROR" = "true" ]; then
782
- MSG="$(jq -r '.message // empty' <"$TMP_JSON" 2>/dev/null || true)"
783
- if [ -n "$MSG" ]; then
784
- echo "fallow-gate: fallow audit runtime error ($MSG), skipping." >&2
785
- else
786
- echo "fallow-gate: fallow audit runtime error, skipping." >&2
787
- fi
788
- exit 0
789
- fi
790
-
791
- if [ "$STATUS" -ne 0 ]; then
792
- ERR_LINE="$(sed -n '1p' "$TMP_ERR" 2>/dev/null || true)"
793
- if [ -n "$ERR_LINE" ]; then
794
- echo "fallow-gate: fallow audit exited $STATUS ($ERR_LINE), skipping." >&2
795
- else
796
- echo "fallow-gate: fallow audit exited $STATUS, skipping." >&2
797
- fi
798
- exit 0
799
- fi
800
-
801
- exit 0
802
- ```
758
+ Behavior you can rely on:
759
+ - Runs only when the intercepted command matches `git commit` or `git push`; otherwise exits 0.
760
+ - 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.
761
+ - Enforces a version floor via `FALLOW_GATE_MIN_VERSION` (default `2.46.0`). Binaries below the floor are blocked with an upgrade hint. Set the env var to the empty string to disable the check.
762
+ - Runs `fallow audit --format json --quiet --explain` 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.
763
+ - On runtime error (`{"error": true, ...}`) or unexpected non-zero exit, fails open with a one-line stderr notice; warn verdicts pass through silently.
803
764
 
804
765
  Codex fallback (add to repo root `AGENTS.md`):
805
766
 
@@ -812,13 +773,13 @@ Keep `fallow audit` in CI alongside this local gate. The hook only runs for Clau
812
773
  ### Remove the hook
813
774
 
814
775
  ```bash
815
- fallow setup-hooks --uninstall
776
+ fallow hooks uninstall --target agent
816
777
  ```
817
778
 
818
779
  Removes the fallow-gate handler from `.claude/settings.json` (preserving any other handlers in the same matcher group), deletes `.claude/hooks/fallow-gate.sh` if it still carries the `# Generated by fallow setup-hooks.` marker, and strips the managed block from `AGENTS.md`. Idempotent: a second run reports `unchanged` / `not present` and exits 0.
819
780
 
820
781
  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.
821
782
 
822
- ### Distinguish from `fallow init --hooks`
783
+ ### Distinguish from `fallow hooks install --target git`
823
784
 
824
- `fallow init --hooks` is a different command: 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 setup-hooks` is the *agent* enforcement path, targeting `.claude/` and `AGENTS.md`. Both can live in the same repo: git hooks catch human commits, the setup-hooks gate catches agent commits.
785
+ `fallow hooks install --target git` is a different target: it scaffolds a shell-level Git pre-commit hook under `.git/hooks/` that runs `fallow` on changed files. That is the *human* enforcement path. `fallow hooks install --target agent` is the *agent* enforcement path, targeting `.claude/` and `AGENTS.md`. Both can live in the same repo: git hooks catch human commits, the agent gate catches agent commits.