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.
- package/dist/index.js +354 -825
- package/dist/template/.opencode/AGENTS.md +12 -0
- package/dist/template/.opencode/command/init.md +198 -34
- package/dist/template/.opencode/context/fallow.md +137 -0
- package/dist/template/.opencode/opencode.json +12 -315
- package/dist/template/.opencode/plugin/memory/compile.ts +171 -186
- package/dist/template/.opencode/plugin/memory/index-generator.ts +118 -133
- package/dist/template/.opencode/plugin/memory/lint.ts +253 -275
- package/dist/template/.opencode/plugin/memory/tools.ts +224 -268
- package/dist/template/.opencode/plugin/memory/validate.ts +154 -164
- package/dist/template/.opencode/plugin/sdk/copilot/responses/tool/web-search-preview.ts +13 -30
- package/dist/template/.opencode/plugin/sdk/copilot/responses/tool/web-search-shared.ts +25 -0
- package/dist/template/.opencode/plugin/sdk/copilot/responses/tool/web-search.ts +17 -34
- package/dist/template/.opencode/plugin/srcwalk.ts +775 -661
- package/dist/template/.opencode/skill/condition-based-waiting/example.ts +15 -2
- package/dist/template/.opencode/skill/fallow/SKILL.md +409 -0
- package/dist/template/.opencode/skill/fallow/references/cli-reference.md +1905 -0
- package/dist/template/.opencode/skill/fallow/references/gotchas.md +644 -0
- package/dist/template/.opencode/skill/fallow/references/patterns.md +791 -0
- package/package.json +1 -1
|
@@ -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.
|