opencodekit 0.23.1 → 0.23.2

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.
@@ -0,0 +1,791 @@
1
+ # Fallow: Common Workflow Patterns & Recipes
2
+
3
+ Step-by-step workflows for common fallow usage scenarios.
4
+
5
+ ---
6
+
7
+ ## Table of Contents
8
+
9
+ - [Full Project Audit](#full-project-audit)
10
+ - [PR Dead Code Check](#pr-dead-code-check)
11
+ - [CI Pipeline Setup](#ci-pipeline-setup)
12
+ - [Incremental Adoption with Baselines](#incremental-adoption-with-baselines)
13
+ - [Monorepo Analysis](#monorepo-analysis)
14
+ - [Duplication Threshold CI Gate](#duplication-threshold-ci-gate)
15
+ - [Migration from knip](#migration-from-knip)
16
+ - [Migration from jscpd](#migration-from-jscpd)
17
+ - [Safe Auto-Fix Workflow](#safe-auto-fix-workflow)
18
+ - [Production vs Full Audit](#production-vs-full-audit)
19
+ - [Debugging False Positives](#debugging-false-positives)
20
+ - [Combined Dead Code + Duplication](#combined-dead-code--duplication)
21
+ - [Custom Plugin Setup](#custom-plugin-setup)
22
+ - [GitHub Code Scanning Integration](#github-code-scanning-integration)
23
+ - [Guard `git push` with a Claude Code PreToolUse hook](#guard-git-push-with-a-claude-code-pretooluse-hook)
24
+
25
+ ---
26
+
27
+ ## Full Project Audit
28
+
29
+ Complete codebase hygiene audit.
30
+
31
+ ### Step 1: Run full analysis
32
+
33
+ ```bash
34
+ fallow dead-code --format json --quiet
35
+ ```
36
+
37
+ ### Step 2: Review issue counts
38
+
39
+ Parse `total_issues` and individual arrays (`unused_files`, `unused_exports`, etc.) to understand the scope.
40
+
41
+ ### Step 3: Find duplication
42
+
43
+ ```bash
44
+ fallow dupes --format json --quiet
45
+ ```
46
+
47
+ ### Step 4: Preview auto-fix
48
+
49
+ ```bash
50
+ fallow fix --dry-run --format json --quiet
51
+ ```
52
+
53
+ ### Step 5: Apply fixes (after user confirmation)
54
+
55
+ ```bash
56
+ fallow fix --yes --format json --quiet
57
+ ```
58
+
59
+ ### Step 6: Verify
60
+
61
+ ```bash
62
+ fallow dead-code --format json --quiet
63
+ ```
64
+
65
+ ---
66
+
67
+ ## PR Dead Code Check
68
+
69
+ Check if a pull request introduces new dead code.
70
+
71
+ ### Step 1: Analyze changed files
72
+
73
+ ```bash
74
+ fallow dead-code --format json --quiet --changed-since main --fail-on-issues
75
+ ```
76
+
77
+ Exit code 1 if the PR introduces new dead code. Exit code 0 if clean.
78
+
79
+ ### Step 2: If issues found, show specifics
80
+
81
+ ```bash
82
+ fallow dead-code --format json --quiet --changed-since main
83
+ ```
84
+
85
+ Parse the JSON to list specific files and exports that became unused.
86
+
87
+ ---
88
+
89
+ ## CI Pipeline Setup
90
+
91
+ ### GitHub Actions: Basic
92
+
93
+ ```yaml
94
+ - name: Dead code check
95
+ run: npx fallow dead-code --fail-on-issues --quiet
96
+ ```
97
+
98
+ ### GitHub Actions: With SARIF Upload
99
+
100
+ ```yaml
101
+ - name: Fallow analysis
102
+ run: npx fallow dead-code --ci > fallow.sarif
103
+ continue-on-error: true # --ci sets --fail-on-issues; continue to upload SARIF even if issues found
104
+
105
+ - name: Upload SARIF
106
+ uses: github/codeql-action/upload-sarif@v3
107
+ with:
108
+ sarif_file: fallow.sarif
109
+ ```
110
+
111
+ ### GitHub Actions: Using the Official Action
112
+
113
+ ```yaml
114
+ - uses: fallow-rs/fallow@v2
115
+ with:
116
+ command: dead-code
117
+ fail-on-issues: true
118
+ changed-since: main
119
+ ```
120
+
121
+ ### GitHub Actions: With Health Score
122
+
123
+ ```yaml
124
+ - uses: fallow-rs/fallow@v2
125
+ with:
126
+ score: true
127
+ changed-since: main
128
+ ```
129
+
130
+ Computes a health score (0-100 with letter grade) in combined mode and enables the health delta header in PR comments.
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
+
157
+ Three additional outputs surface silent failures in the action's PR comment / review steps. `outputs.changed-files-unavailable` (`true`/`false`, default `false`) signals that the analyze step could not enumerate PR-changed files (transient GitHub API failure, expired token, missing permissions), so analysis ran against the full codebase. `outputs.post-skipped-reason` (`none`/`pagination_failure`) signals the Post review comments step aborted to avoid duplicate threads. `outputs.dedup-lookup-failed` (`true`/`false`) signals a dedup lookup failed on either the Post PR comment or Post review comments step. All three are always emitted regardless of which failure path was taken, so downstream `if:` gates can match positively without absent-vs-false ambiguity. Gate on these to detect degraded posting state and re-run the action.
158
+
159
+ ### GitHub Actions: Inline PR Annotations (No Advanced Security)
160
+
161
+ 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.
162
+
163
+ ```yaml
164
+ - uses: fallow-rs/fallow@v2
165
+ with:
166
+ command: dead-code
167
+ changed-since: main
168
+ annotations: true
169
+ max-annotations: 50 # default: 50, limits annotation count
170
+ ```
171
+
172
+ Annotations appear as inline warnings on the PR diff. They work with all commands (`dead-code`, `dupes`, `health`, and the default combined mode). The `max-annotations` input prevents annotation flooding on large projects.
173
+
174
+ ### GitHub Actions: PR-Scoped Check
175
+
176
+ ```yaml
177
+ - name: Check for new dead code
178
+ run: npx fallow dead-code --format json --quiet --changed-since ${{ github.event.pull_request.base.sha }} --fail-on-issues
179
+ ```
180
+
181
+ ### GitHub Actions: Duplication Gate
182
+
183
+ ```yaml
184
+ - name: Duplication check
185
+ run: npx fallow dupes --format json --quiet --threshold 5 --mode mild
186
+ ```
187
+
188
+ Fails if overall duplication exceeds 5%.
189
+
190
+ ### GitHub Actions: PR-Scoped Duplication Check
191
+
192
+ ```yaml
193
+ - name: Check duplication in changed files
194
+ run: npx fallow dupes --format json --quiet --changed-since ${{ github.event.pull_request.base.sha }}
195
+ ```
196
+
197
+ Only reports duplication in files modified by the PR.
198
+
199
+ ### GitLab CI: Using the Official Template
200
+
201
+ ```yaml
202
+ include:
203
+ - remote: 'https://raw.githubusercontent.com/fallow-rs/fallow/main/ci/gitlab-ci.yml'
204
+
205
+ fallow:
206
+ extends: .fallow
207
+ variables:
208
+ FALLOW_COMMAND: "dead-code"
209
+ FALLOW_FAIL_ON_ISSUES: "true"
210
+ ```
211
+
212
+ Generates Code Quality reports (inline MR annotations) automatically. In MR pipelines, `--changed-since` is automatically set to the target branch — no manual configuration needed.
213
+
214
+ If runners cannot reach `raw.githubusercontent.com`, run `fallow ci-template gitlab --vendor`, commit the generated `ci/` and `action/` files, and use GitLab's local include syntax:
215
+
216
+ ```yaml
217
+ include:
218
+ - local: 'ci/gitlab-ci.yml'
219
+ ```
220
+
221
+ ### GitLab CI: With MR Summary Comments
222
+
223
+ ```yaml
224
+ include:
225
+ - remote: 'https://raw.githubusercontent.com/fallow-rs/fallow/main/ci/gitlab-ci.yml'
226
+
227
+ fallow:
228
+ extends: .fallow
229
+ variables:
230
+ FALLOW_COMMENT: "true"
231
+ FALLOW_SUMMARY_SCOPE: "diff"
232
+ ```
233
+
234
+ Posts a summary comment on the MR with issue counts and findings. In MR pipelines, `--changed-since` is auto-detected from `$CI_MERGE_REQUEST_TARGET_BRANCH_NAME`, so only issues from changed files are reported. `FALLOW_SUMMARY_SCOPE: "diff"` also hides project-level dependency/catalog/override findings whose anchor line is outside the diff. Requires `GITLAB_TOKEN` CI/CD variable (project access token with `api` scope); `CI_JOB_TOKEN` is read-only for MR notes in the official GitLab API.
235
+
236
+ ### GitLab CI: With Inline Code Review Comments
237
+
238
+ ```yaml
239
+ include:
240
+ - remote: 'https://raw.githubusercontent.com/fallow-rs/fallow/main/ci/gitlab-ci.yml'
241
+
242
+ fallow:
243
+ extends: .fallow
244
+ variables:
245
+ FALLOW_REVIEW: "true"
246
+ FALLOW_REVIEW_GUIDANCE: "true"
247
+ ```
248
+
249
+ Posts inline review comments directly on the MR diff lines where issues were found. `FALLOW_REVIEW_GUIDANCE: "true"` adds collapsed "What to do" guidance blocks to each inline finding. This gives developers precise feedback without leaving the code review flow. Can be combined with `FALLOW_COMMENT: "true"` for both a summary and inline comments. Requires `GITLAB_TOKEN`.
250
+
251
+ ### GitLab CI: Combined MR Comments + Review
252
+
253
+ ```yaml
254
+ include:
255
+ - remote: 'https://raw.githubusercontent.com/fallow-rs/fallow/main/ci/gitlab-ci.yml'
256
+
257
+ fallow:
258
+ extends: .fallow
259
+ variables:
260
+ FALLOW_COMMENT: "true"
261
+ FALLOW_SUMMARY_SCOPE: "diff"
262
+ FALLOW_REVIEW: "true"
263
+ FALLOW_REVIEW_GUIDANCE: "true"
264
+ FALLOW_FAIL_ON_ISSUES: "true"
265
+ ```
266
+
267
+ Posts both a summary comment and inline review comments on the MR. `FALLOW_SUMMARY_SCOPE: "diff"` only affects the sticky summary; inline review comments remain anchored to diff lines. The template auto-detects the package manager (npm/pnpm/yarn) from lockfiles, so review comments show the correct commands for the project (e.g., `pnpm remove` instead of `npm uninstall`).
268
+
269
+ ### GitLab CI: With Health Score and Trend
270
+
271
+ ```yaml
272
+ include:
273
+ - remote: 'https://raw.githubusercontent.com/fallow-rs/fallow/main/ci/gitlab-ci.yml'
274
+
275
+ fallow:
276
+ extends: .fallow
277
+ variables:
278
+ FALLOW_SCORE: "true"
279
+ FALLOW_TREND: "true"
280
+ FALLOW_COMMENT: "true"
281
+ ```
282
+
283
+ Computes the health score and compares against saved snapshots. The MR comment includes a health delta header showing score changes. `FALLOW_TREND` implies `FALLOW_SCORE`.
284
+
285
+ ### GitLab CI: Manual (Without Template)
286
+
287
+ ```yaml
288
+ fallow:
289
+ image: node:20-slim
290
+ script:
291
+ - npx fallow dead-code --fail-on-issues --quiet --format json > fallow-results.json
292
+ artifacts:
293
+ paths:
294
+ - fallow-results.json
295
+ ```
296
+
297
+ ---
298
+
299
+ ## Incremental Adoption with Baselines
300
+
301
+ For large projects with existing dead code. Adopt gradually without fixing everything at once.
302
+
303
+ ### Step 1: Save current state as baseline
304
+
305
+ ```bash
306
+ fallow dead-code --format json --quiet --save-baseline fallow-baselines/dead-code.json
307
+ ```
308
+
309
+ ### Step 2: Commit the baseline
310
+
311
+ ```bash
312
+ git add fallow-baselines/dead-code.json
313
+ git commit -m "chore: add fallow baseline"
314
+ ```
315
+
316
+ ### Step 3: CI only fails on NEW issues
317
+
318
+ ```bash
319
+ fallow dead-code --format json --quiet --baseline fallow-baselines/dead-code.json --fail-on-issues
320
+ ```
321
+
322
+ ### Step 4: Gradually fix and update baseline
323
+
324
+ As you fix existing issues, regenerate the baseline:
325
+
326
+ ```bash
327
+ fallow dead-code --format json --quiet --save-baseline fallow-baselines/dead-code.json
328
+ ```
329
+
330
+ ### Duplication baseline
331
+
332
+ Same pattern works for duplication:
333
+
334
+ ```bash
335
+ fallow dupes --format json --quiet --save-baseline fallow-baselines/dupes.json
336
+ fallow dupes --format json --quiet --baseline fallow-baselines/dupes.json --threshold 5
337
+ ```
338
+
339
+ ---
340
+
341
+ ## Monorepo Analysis
342
+
343
+ ### Analyze the full monorepo
344
+
345
+ ```bash
346
+ fallow dead-code --format json --quiet
347
+ ```
348
+
349
+ Fallow auto-detects workspaces from `package.json` workspaces or `pnpm-workspace.yaml`.
350
+
351
+ ### Analyze a single package
352
+
353
+ ```bash
354
+ fallow dead-code --format json --quiet --workspace my-package
355
+ ```
356
+
357
+ Full cross-workspace graph is built (so imports between packages are resolved), but only issues in `my-package` are reported.
358
+
359
+ ### Per-package CI
360
+
361
+ Run analysis for each workspace package separately:
362
+
363
+ ```bash
364
+ fallow dead-code --format json --quiet --workspace package-a --fail-on-issues
365
+ fallow dead-code --format json --quiet --workspace package-b --fail-on-issues
366
+ ```
367
+
368
+ ### List all discovered files across workspaces
369
+
370
+ ```bash
371
+ fallow list --files --format json --quiet
372
+ ```
373
+
374
+ ---
375
+
376
+ ## Duplication Threshold CI Gate
377
+
378
+ Enforce a maximum duplication percentage.
379
+
380
+ ### Step 1: Measure current duplication
381
+
382
+ ```bash
383
+ fallow dupes --format json --quiet
384
+ ```
385
+
386
+ Check `duplication_percentage` in the JSON output.
387
+
388
+ ### Step 2: Set threshold slightly above current
389
+
390
+ If current duplication is 3.8%, set threshold to 5%:
391
+
392
+ ```bash
393
+ fallow dupes --format json --quiet --threshold 5
394
+ ```
395
+
396
+ Exits with code 1 if duplication exceeds 5%.
397
+
398
+ ### Step 3: Tighten over time
399
+
400
+ As you reduce duplication, lower the threshold.
401
+
402
+ ### Cross-directory only
403
+
404
+ To ignore duplication within the same directory (local helpers, similar test files):
405
+
406
+ ```bash
407
+ fallow dupes --format json --quiet --threshold 5 --skip-local
408
+ ```
409
+
410
+ ---
411
+
412
+ ## Migration from knip
413
+
414
+ ### Step 1: Preview migration
415
+
416
+ ```bash
417
+ fallow migrate --dry-run
418
+ ```
419
+
420
+ Shows what config would be generated. Auto-detects `knip.json`, `.knip.json`, `knip.jsonc`, `.knip.jsonc`, or `package.json#knip`.
421
+
422
+ ### Step 2: Apply migration
423
+
424
+ ```bash
425
+ fallow migrate
426
+ ```
427
+
428
+ Creates `.fallowrc.json` with mapped settings:
429
+ - knip `rules`/`exclude`/`include` → fallow `rules` (error/warn/off)
430
+ - knip `ignore` → fallow `ignorePatterns`
431
+ - knip `ignoreDependencies` → fallow `ignoreDependencies`
432
+ - 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)
433
+ - Unmappable fields generate warnings with suggestions
434
+
435
+ ### Step 3: Compare results
436
+
437
+ ```bash
438
+ # Run fallow
439
+ fallow dead-code --format json --quiet
440
+
441
+ # Compare with knip output
442
+ npx knip --reporter json
443
+ ```
444
+
445
+ ### Step 4: Remove knip config
446
+
447
+ Once satisfied, remove the old `knip.json` and uninstall knip.
448
+
449
+ ---
450
+
451
+ ## Migration from jscpd
452
+
453
+ ### Step 1: Preview migration
454
+
455
+ ```bash
456
+ fallow migrate --dry-run
457
+ ```
458
+
459
+ Auto-detects `.jscpd.json` or `package.json#jscpd`.
460
+
461
+ ### Step 2: Apply migration
462
+
463
+ ```bash
464
+ fallow migrate
465
+ ```
466
+
467
+ Maps jscpd settings:
468
+ - `minTokens` → `duplicates.minTokens`
469
+ - `minLines` → `duplicates.minLines`
470
+ - `threshold` → `duplicates.threshold`
471
+ - `mode` → `duplicates.mode`
472
+
473
+ ### Step 3: Compare results
474
+
475
+ ```bash
476
+ fallow dupes --format json --quiet
477
+ ```
478
+
479
+ ### Detection mode mapping
480
+
481
+ | jscpd | fallow |
482
+ |-------|--------|
483
+ | Default (exact tokens) | `strict` |
484
+ | — | `mild` (fallow default, syntax normalized) |
485
+ | — | `weak` (literal normalization) |
486
+ | — | `semantic` (variable rename detection) |
487
+
488
+ ---
489
+
490
+ ## Safe Auto-Fix Workflow
491
+
492
+ ### Step 1: Dry-run first
493
+
494
+ ```bash
495
+ fallow fix --dry-run --format json --quiet
496
+ ```
497
+
498
+ ### Step 2: Review each proposed change
499
+
500
+ Parse the JSON `changes` array. Each entry shows:
501
+ - `path`: file to be modified
502
+ - `action`: what will happen (`remove_export`, `remove_dependency`)
503
+ - `name`: the symbol or dependency being removed
504
+ - `line`: the line number
505
+
506
+ ### Step 3: Confirm with user before applying
507
+
508
+ Show the proposed changes. Wait for user confirmation.
509
+
510
+ ### Step 4: Apply
511
+
512
+ ```bash
513
+ fallow fix --yes --format json --quiet
514
+ ```
515
+
516
+ ### Step 5: Verify
517
+
518
+ ```bash
519
+ fallow dead-code --format json --quiet
520
+ ```
521
+
522
+ ### Step 6: Run project tests
523
+
524
+ After auto-fix, always run the project's test suite to verify nothing broke.
525
+
526
+ ---
527
+
528
+ ## Production vs Full Audit
529
+
530
+ ### Full audit (default)
531
+
532
+ ```bash
533
+ fallow dead-code --format json --quiet
534
+ ```
535
+
536
+ Includes all files, all scripts, all dependencies (including devDependencies).
537
+
538
+ ### Production audit
539
+
540
+ ```bash
541
+ fallow dead-code --format json --quiet --production
542
+ ```
543
+
544
+ Differences:
545
+ - Excludes: `*.test.*`, `*.spec.*`, `*.stories.*`, `__tests__/**`, `__mocks__/**`
546
+ - Only analyzes: `start`, `build`, `serve`, `preview`, `prepare` scripts
547
+ - Skips: unused devDependency detection
548
+ - Adds: type-only production dependency detection
549
+
550
+ Use production mode for:
551
+ - Checking what ships to users
552
+ - Finding dependencies that should be devDependencies
553
+ - CI pipelines focused on production bundle
554
+
555
+ Use full mode for:
556
+ - Complete codebase hygiene
557
+ - Finding unused test utilities
558
+ - Auditing devDependency usage
559
+
560
+ ---
561
+
562
+ ## Debugging False Positives
563
+
564
+ ### Trace an export's usage chain
565
+
566
+ ```bash
567
+ fallow dead-code --format json --quiet --trace src/utils.ts:myFunction
568
+ ```
569
+
570
+ Shows where `myFunction` is imported (or not imported) and why it's flagged.
571
+
572
+ ### Trace all edges for a file
573
+
574
+ ```bash
575
+ fallow dead-code --format json --quiet --trace-file src/utils.ts
576
+ ```
577
+
578
+ Shows all imports/exports for the file and their resolution status.
579
+
580
+ ### Trace a dependency
581
+
582
+ ```bash
583
+ fallow dead-code --format json --quiet --trace-dependency lodash
584
+ ```
585
+
586
+ Shows all files that import lodash.
587
+
588
+ ### If the trace shows it IS used
589
+
590
+ The export might be consumed through a pattern fallow can't resolve (fully dynamic import, reflection). Add a suppression:
591
+
592
+ ```typescript
593
+ // fallow-ignore-next-line unused-export
594
+ export const dynamicallyUsed = createHandler();
595
+ ```
596
+
597
+ ### If the trace shows it's NOT used
598
+
599
+ The export is genuinely unused. Consider removing it or marking it as intentionally kept:
600
+
601
+ ```typescript
602
+ // fallow-ignore-next-line unused-export
603
+ export const publicApi = createWidget(); // Used by external consumers
604
+ ```
605
+
606
+ ---
607
+
608
+ ## Combined Dead Code + Duplication
609
+
610
+ Cross-reference dead code with duplication findings to find high-priority cleanup targets.
611
+
612
+ ### Step 1: Run combined analysis
613
+
614
+ ```bash
615
+ fallow dead-code --format json --quiet --include-dupes
616
+ ```
617
+
618
+ This adds duplication context to dead code findings, identifying clone instances that exist in unused files or overlap with unused exports.
619
+
620
+ ### Step 2: Prioritize cleanup
621
+
622
+ Focus on findings that are BOTH dead code and duplicated:
623
+ - Unused files containing duplicate code → delete the file entirely
624
+ - Unused exports that are clones of other exports → remove the duplicate
625
+
626
+ ---
627
+
628
+ ## Custom Plugin Setup
629
+
630
+ For frameworks not covered by the 118 built-in plugins.
631
+
632
+ ### Option 1: Inline framework config
633
+
634
+ ```jsonc
635
+ // .fallowrc.json
636
+ {
637
+ "framework": [
638
+ {
639
+ "name": "my-framework",
640
+ "enablers": ["my-framework"],
641
+ "entryPoints": ["src/routes/**/*.ts", "src/middleware/**/*.ts"]
642
+ }
643
+ ]
644
+ }
645
+ ```
646
+
647
+ ### Option 2: External plugin file
648
+
649
+ Create `.fallow/plugins/my-framework.jsonc`:
650
+
651
+ ```jsonc
652
+ {
653
+ "name": "my-framework",
654
+ "detection": { "dependency": "my-framework" },
655
+ "entryPoints": ["src/routes/**/*.ts"],
656
+ "alwaysUsedFiles": ["src/bootstrap.ts"],
657
+ "usedExports": {
658
+ "src/config.ts": ["default"]
659
+ },
660
+ "toolingDependencies": ["my-framework-cli"]
661
+ }
662
+ ```
663
+
664
+ ### Option 3: Plugin directory
665
+
666
+ ```jsonc
667
+ // .fallowrc.json
668
+ {
669
+ "plugins": ["tools/plugins/"]
670
+ }
671
+ ```
672
+
673
+ Place `.jsonc`, `.json`, or `.toml` plugin files in that directory.
674
+
675
+ ---
676
+
677
+ ## GitHub Code Scanning Integration
678
+
679
+ Upload fallow results to GitHub's Code Scanning dashboard.
680
+
681
+ ### Step 1: Generate SARIF output
682
+
683
+ ```bash
684
+ fallow dead-code --format sarif --quiet > fallow.sarif
685
+ ```
686
+
687
+ ### Step 2: Upload via GitHub Action
688
+
689
+ ```yaml
690
+ - name: Upload SARIF
691
+ uses: github/codeql-action/upload-sarif@v3
692
+ with:
693
+ sarif_file: fallow.sarif
694
+ ```
695
+
696
+ ### All-in-one with `--ci`
697
+
698
+ ```bash
699
+ fallow dead-code --ci > fallow.sarif
700
+ ```
701
+
702
+ The `--ci` flag is equivalent to `--format sarif --fail-on-issues --quiet`. Note: `--fail-on-issues` means exit code 1 if issues exist, in CI scripts use `continue-on-error: true` or `|| true` to ensure the SARIF upload step still runs.
703
+
704
+ ---
705
+
706
+ ## Guard `git push` with a Claude Code PreToolUse hook
707
+
708
+ Use this when Claude Code is allowed to run Git commands in a repository that already uses fallow.
709
+
710
+ The pattern is a local agent gate, not a Git hook. Claude Code intercepts its own `Bash` tool calls before execution. When Claude tries `git commit` or `git push`, the hook runs:
711
+
712
+ ```bash
713
+ fallow audit --format json --quiet --explain
714
+ ```
715
+
716
+ Behavior:
717
+
718
+ - `pass`: allow the command
719
+ - `warn`: allow the command
720
+ - `fail`: exit 2 and write the raw audit JSON to stderr
721
+ - runtime error JSON like `{ "error": true, ... }`: fail open, do not block
722
+
723
+ Because Claude receives stderr as tool feedback on a blocked `PreToolUse` call, it can read the structured findings (including `_meta.docs` links and `actions`), fix the code, and retry the Git command.
724
+
725
+ Install it automatically:
726
+
727
+ ```bash
728
+ fallow hooks install --target agent
729
+ ```
730
+
731
+ Remove it later with:
732
+
733
+ ```bash
734
+ fallow hooks uninstall --target agent
735
+ ```
736
+
737
+ Manual files:
738
+
739
+ ### `.claude/settings.json`
740
+
741
+ ```json
742
+ {
743
+ "$schema": "https://json.schemastore.org/claude-code-settings.json",
744
+ "hooks": {
745
+ "PreToolUse": [
746
+ {
747
+ "matcher": "Bash",
748
+ "hooks": [
749
+ {
750
+ "type": "command",
751
+ "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/fallow-gate.sh"
752
+ }
753
+ ]
754
+ }
755
+ ]
756
+ }
757
+ }
758
+ ```
759
+
760
+ ### `.claude/hooks/fallow-gate.sh`
761
+
762
+ 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).
763
+
764
+ Behavior you can rely on:
765
+ - Runs only when the intercepted command matches `git commit` or `git push`; otherwise exits 0.
766
+ - 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.
767
+ - 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.
768
+ - 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.
769
+ - On runtime error (`{"error": true, ...}`) or unexpected non-zero exit, fails open with a one-line stderr notice; warn verdicts pass through silently.
770
+
771
+ Codex fallback (add to repo root `AGENTS.md`):
772
+
773
+ ```md
774
+ Before any `git commit` or `git push`, run `fallow audit --format json --quiet --explain`. If the verdict is `fail`, fix the reported findings before retrying. Treat JSON runtime errors like `{ "error": true, ... }` as non-blocking.
775
+ ```
776
+
777
+ Keep `fallow audit` in CI alongside this local gate. The hook only runs for Claude Code, not for human pushes or other agents, so it is a reinforcement layer rather than a replacement for server-side enforcement.
778
+
779
+ ### Remove the hook
780
+
781
+ ```bash
782
+ fallow hooks uninstall --target agent
783
+ ```
784
+
785
+ 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.
786
+
787
+ 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.
788
+
789
+ ### Distinguish from `fallow hooks install --target git`
790
+
791
+ `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.