fallow 2.103.0 → 2.104.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 +2 -2
- package/package.json +10 -10
- package/schema.json +17 -0
- package/skills/fallow/SKILL.md +6 -6
- package/skills/fallow/references/cli-reference.md +15 -12
- package/skills/fallow/references/gotchas.md +7 -1
- package/skills/fallow/references/patterns.md +1 -1
- package/types/output-contract.d.ts +235 -19
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@ Fallow turns a JS/TS repository into a trusted quality report: health score, cha
|
|
|
12
12
|
|
|
13
13
|
It helps you answer: what changed, what got riskier, what should be reviewed, what should be refactored, and what can be safely removed. No AI inside the analyzer. Fallow produces deterministic findings, typed output contracts, and traceable explanations that downstream tools can trust.
|
|
14
14
|
|
|
15
|
-
Static analysis is free and open source. An optional paid runtime layer (Fallow Runtime) adds production execution evidence. Rust-native, sub-second,
|
|
15
|
+
Static analysis is free and open source. An optional paid runtime layer (Fallow Runtime) adds production execution evidence. Rust-native, sub-second, 123 framework plugins, no Node.js runtime dependency for analysis. fallow's edge is doing more in one pass: dead code, duplication, dependencies, complexity, architecture, and security in a single tool. For head-to-head timings against [knip](https://knip.dev) and [jscpd](https://github.com/kucherenko/jscpd), see the [benchmarks](https://github.com/fallow-rs/fallow/tree/main/benchmarks): fallow is faster than knip on smaller projects, knip is faster on several larger repos, and jscpd's Rust rewrite is faster for raw duplication scanning.
|
|
16
16
|
|
|
17
17
|
## Installation
|
|
18
18
|
|
|
@@ -107,7 +107,7 @@ Swap `npx` for `pnpm exec` / `yarn` / `bunx` to match your package manager. If `
|
|
|
107
107
|
|
|
108
108
|
## Framework support
|
|
109
109
|
|
|
110
|
-
|
|
110
|
+
123 built-in plugins covering Next.js, Nuxt, Remix, Qwik, SvelteKit, Gatsby, Astro, Angular, NestJS, AdonisJS, Ember, Expo Router, Vite, Webpack, Vitest, Jest, Playwright, Cypress, Storybook, ESLint, TypeScript, Tailwind, UnoCSS, Prisma, Drizzle, Convex, Turborepo, Hardhat, and many more. Auto-detected from your `package.json`.
|
|
111
111
|
|
|
112
112
|
## Configuration
|
|
113
113
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fallow",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.104.0",
|
|
4
4
|
"description": "Deterministic codebase intelligence for TypeScript and JavaScript. Quality, risk, architecture, dependencies, duplication, and safe cleanup evidence for humans, CI, and agents. Optional runtime intelligence layer (Fallow Runtime) adds production execution evidence. Rust-native, sub-second, zero-config framework support.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -80,16 +80,16 @@
|
|
|
80
80
|
"detect-libc": "2.1.2"
|
|
81
81
|
},
|
|
82
82
|
"devDependencies": {
|
|
83
|
-
"@tanstack/intent": "0.
|
|
83
|
+
"@tanstack/intent": "0.3.2"
|
|
84
84
|
},
|
|
85
85
|
"optionalDependencies": {
|
|
86
|
-
"@fallow-cli/darwin-arm64": "2.
|
|
87
|
-
"@fallow-cli/darwin-x64": "2.
|
|
88
|
-
"@fallow-cli/linux-x64-gnu": "2.
|
|
89
|
-
"@fallow-cli/linux-arm64-gnu": "2.
|
|
90
|
-
"@fallow-cli/linux-x64-musl": "2.
|
|
91
|
-
"@fallow-cli/linux-arm64-musl": "2.
|
|
92
|
-
"@fallow-cli/win32-arm64-msvc": "2.
|
|
93
|
-
"@fallow-cli/win32-x64-msvc": "2.
|
|
86
|
+
"@fallow-cli/darwin-arm64": "2.104.0",
|
|
87
|
+
"@fallow-cli/darwin-x64": "2.104.0",
|
|
88
|
+
"@fallow-cli/linux-x64-gnu": "2.104.0",
|
|
89
|
+
"@fallow-cli/linux-arm64-gnu": "2.104.0",
|
|
90
|
+
"@fallow-cli/linux-x64-musl": "2.104.0",
|
|
91
|
+
"@fallow-cli/linux-arm64-musl": "2.104.0",
|
|
92
|
+
"@fallow-cli/win32-arm64-msvc": "2.104.0",
|
|
93
|
+
"@fallow-cli/win32-x64-msvc": "2.104.0"
|
|
94
94
|
}
|
|
95
95
|
}
|
package/schema.json
CHANGED
|
@@ -194,6 +194,9 @@
|
|
|
194
194
|
"dynamic-segment-name-conflict": "error"
|
|
195
195
|
}
|
|
196
196
|
},
|
|
197
|
+
"unusedComponentProps": {
|
|
198
|
+
"$ref": "#/$defs/UnusedComponentPropsConfig"
|
|
199
|
+
},
|
|
197
200
|
"boundaries": {
|
|
198
201
|
"$ref": "#/$defs/BoundaryConfig",
|
|
199
202
|
"default": {
|
|
@@ -1227,6 +1230,20 @@
|
|
|
1227
1230
|
}
|
|
1228
1231
|
]
|
|
1229
1232
|
},
|
|
1233
|
+
"UnusedComponentPropsConfig": {
|
|
1234
|
+
"description": "Options for the `unused-component-props` rule.\n\nLets a project exempt component props whose local destructure binding name\nmatches a regex from `unused-component-props`, honoring the\n\"accepted-but-intentionally-unused\" leading-underscore convention (Svelte 5\n`$props()`, React destructure) that mirrors TypeScript `noUnusedParameters`\nand ESLint `@typescript-eslint/no-unused-vars` `varsIgnorePattern` /\n`argsIgnorePattern`. Opt-in; an unset `ignorePattern` leaves the rule's\nbehavior unchanged.",
|
|
1235
|
+
"type": "object",
|
|
1236
|
+
"properties": {
|
|
1237
|
+
"ignorePattern": {
|
|
1238
|
+
"description": "Regex matched against each declared prop's LOCAL destructure binding name\n(e.g. `_stage` in `let { stage: _stage } = $props()`), which falls back\nto the public prop name when there is no alias. A prop whose local name\nmatches is treated as intentionally unused and never reported as\n`unused-component-props`. Matching is unanchored (substring), like\nESLint's `RegExp.test`, so anchor with `^_` to match a leading\nunderscore. Compiled and validated at config load (an invalid regex fails\nload). Applies to Vue, Svelte, Astro, and React/Preact props.",
|
|
1239
|
+
"type": [
|
|
1240
|
+
"string",
|
|
1241
|
+
"null"
|
|
1242
|
+
]
|
|
1243
|
+
}
|
|
1244
|
+
},
|
|
1245
|
+
"additionalProperties": false
|
|
1246
|
+
},
|
|
1230
1247
|
"BoundaryConfig": {
|
|
1231
1248
|
"description": "Architecture boundary configuration.",
|
|
1232
1249
|
"type": "object",
|
package/skills/fallow/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: fallow
|
|
3
|
-
description: Codebase intelligence for JavaScript and TypeScript. Free static layer reports quality, changed-code risk, cleanup opportunities (unused files, exports, types, dependencies), code duplication, circular dependencies, complexity hotspots, architecture boundary violations, feature flag patterns, and opt-in security candidates. Runtime coverage merges production execution data into the same health report for hot-path review, cold-path deletion confidence, and stale-flag evidence, with a single local capture available by default and continuous/cloud runtime monitoring available as an optional mode.
|
|
3
|
+
description: Codebase intelligence for JavaScript and TypeScript. Free static layer reports quality, changed-code risk, cleanup opportunities (unused files, exports, types, dependencies), code duplication, circular dependencies, complexity hotspots, architecture boundary violations, feature flag patterns, and opt-in security candidates. Runtime coverage merges production execution data into the same health report for hot-path review, cold-path deletion confidence, and stale-flag evidence, with a single local capture available by default and continuous/cloud runtime monitoring available as an optional mode. 123 framework plugins, zero configuration, sub-second static analysis. Use when asked to analyze code health, audit PR risk, find cleanup opportunities or unused code, detect duplicates, check circular dependencies, audit complexity, check architecture boundaries, detect feature flags, surface security candidates, clean up the codebase, auto-fix issues, merge runtime coverage, or run fallow.
|
|
4
4
|
license: MIT
|
|
5
5
|
metadata:
|
|
6
6
|
author: Bart Waardenburg
|
|
@@ -10,7 +10,7 @@ metadata:
|
|
|
10
10
|
|
|
11
11
|
# Fallow: codebase intelligence for JavaScript and TypeScript
|
|
12
12
|
|
|
13
|
-
Codebase intelligence for JavaScript and TypeScript. The free static layer reports quality, changed-code risk, cleanup opportunities, circular dependencies, code duplication, complexity hotspots, architecture boundary violations, feature flag patterns, and opt-in security candidates. Runtime coverage merges production execution data into the same `fallow health` report for hot-path review, cold-path deletion confidence, and stale-flag evidence, with a single local capture available by default and continuous/cloud runtime monitoring available as an optional mode.
|
|
13
|
+
Codebase intelligence for JavaScript and TypeScript. The free static layer reports quality, changed-code risk, cleanup opportunities, circular dependencies, code duplication, complexity hotspots, architecture boundary violations, feature flag patterns, and opt-in security candidates. Runtime coverage merges production execution data into the same `fallow health` report for hot-path review, cold-path deletion confidence, and stale-flag evidence, with a single local capture available by default and continuous/cloud runtime monitoring available as an optional mode. 123 framework plugins, zero configuration, sub-second static analysis.
|
|
14
14
|
|
|
15
15
|
## When to Use
|
|
16
16
|
- Finding cleanup opportunities (unused files, exports, types, enum/class members)
|
|
@@ -66,7 +66,6 @@ cargo install fallow-cli # build from source
|
|
|
66
66
|
## Onboarding And Insight
|
|
67
67
|
Offer setup only after a human-requested analysis shows findings and all signals match: `fallow config --path` exits 3, not CI, not a pipeline format, `fallow impact --format json --quiet` has `onboarding_declined: false`, and no offer happened this session. Ask after showing value. Choices: guard commits and PRs, baseline the existing backlog and clean by category, add AGENTS.md guidance, or keep as-is. On decline, run `fallow init --decline --quiet` and stay silent for this project. Mutate only after consent. For guards, inspect `fallow hooks status --format json --quiet`, then use `fallow hooks install --target agent` and `fallow hooks install --target git`; for large backlogs, pair the gate with `--save-baseline` / new-only guidance. Offer `fallow impact enable` as local-only value tracking, never as telemetry; also offer it once on already-configured projects when `fallow impact status --format json` has `enabled: false` and `explicit_decision: false`, and record a no with `fallow impact disable --quiet`. Surface value on clear events: if the agent gate blocked a commit or push and a later retry succeeded, mention what was contained; when `next_steps` carries id `impact-report`, run its command and relay the non-zero numbers to the user in one line. On request, summarize non-zero Impact counts. Ask about telemetry only after such a win, only if `fallow telemetry status --format json` has `explicit_decision: false`, and never run `fallow telemetry enable`.
|
|
68
68
|
## Task Cheat Sheet
|
|
69
|
-
|
|
70
69
|
Route by intent before reaching for the big analysis commands. Same matrix as `fallow schema` (`task_matrix`) and the generated AGENTS.md section.
|
|
71
70
|
|
|
72
71
|
<!-- generated:task-matrix:start -->
|
|
@@ -211,6 +210,7 @@ When using fallow via MCP (`fallow-mcp`), the following tools are available:
|
|
|
211
210
|
| `get_blast_radius` | runtime-coverage | freemium | `coverage`, `group_by` | Runtime-context slice for blast-radius review. Same params as `check_runtime_coverage`; read `runtime_coverage.blast_radius` for stable `fallow:blast:<hash>` IDs, caller counts, traffic-weighted caller reach, optional cloud deploy touch counts, and low/medium/high risk bands. |
|
|
212
211
|
| `get_importance` | runtime-coverage | freemium | `coverage`, `group_by` | Runtime-context slice for production-importance review. Same params as `check_runtime_coverage`; read `runtime_coverage.importance` for stable `fallow:importance:<hash>` IDs, invocations, cyclomatic complexity, owner count, 0-100 score, and templated reason. |
|
|
213
212
|
| `get_cleanup_candidates` | runtime-coverage | freemium | `coverage`, `group_by` | Runtime-context slice for cleanup review. Same params as `check_runtime_coverage`; read `runtime_coverage.findings` for `safe_to_delete`, `review_required`, `low_traffic`, and `coverage_unavailable`. |
|
|
213
|
+
| `get_token_blast_radius` | analysis | free | - | Design-token blast radius for Tailwind v4 @theme tokens AND CSS-in-JS defineVars/createTheme-family token definitions: per token, a consumer_count (static lower bound) and a capped located consumers[] sample tagged theme-var/css-var/utility/apply (Tailwind) or js-member (CSS-in-JS cross-module member access); descriptive context for sizing a token change, never a deletion gate |
|
|
214
214
|
| `audit` | analysis | free | `gate`, `base`, `max_crap`, `coverage`, `runtime_coverage` | Combined dead-code + complexity + duplication for changed files, returns verdict. Set `gate` to `"new-only"` or `"all"`. Optional `runtime_coverage` (V8 dir / V8 JSON / Istanbul JSON) folds runtime findings into the same call; `min_invocations_hot` tunes the hot-path threshold (default 100). Runtime evidence appears under the audit `complexity` sub-result, including `coverage_intelligence` when combined evidence yields actionable recommendations. |
|
|
215
215
|
| `decision_surface` | analysis | free | `base`, `max_decisions`, `workspace` | Surface the few consequential structural decisions a change embeds (coupling, public API, dependency), each as a judgment question with the routed expert; ranked, capped, and signal_id-anchored |
|
|
216
216
|
| `fallow_explain` | introspection | free | `issue_type` | Explain one issue type without running analysis. Required `issue_type`; returns rationale, examples, fix guidance, and docs URL |
|
|
@@ -297,7 +297,7 @@ fallow list --entry-points --format json --quiet
|
|
|
297
297
|
fallow list --plugins --format json --quiet
|
|
298
298
|
```
|
|
299
299
|
|
|
300
|
-
Shows detected entry points and active framework plugins (
|
|
300
|
+
Shows detected entry points and active framework plugins (123 built-in: Next.js, Vite, Ember, Wuchale, Jest, Storybook, Tailwind, PandaCSS, Contentlayer, tap, tsd, etc.).
|
|
301
301
|
|
|
302
302
|
### Production-only analysis
|
|
303
303
|
```bash
|
|
@@ -443,7 +443,7 @@ When `--format json` is active and exit code is 2, errors are emitted as JSON on
|
|
|
443
443
|
|
|
444
444
|
## Configuration
|
|
445
445
|
|
|
446
|
-
Fallow reads config from project root: `.fallowrc.json` > `.fallowrc.jsonc` > `fallow.toml` > `.fallow.toml`. Both `.fallowrc.json` and `.fallowrc.jsonc` accept JSON-with-comments syntax (same parser); the `.jsonc` extension lets editors auto-detect JSONC syntax highlighting. Most projects work with zero configuration thanks to
|
|
446
|
+
Fallow reads config from project root: `.fallowrc.json` > `.fallowrc.jsonc` > `fallow.toml` > `.fallow.toml`. Both `.fallowrc.json` and `.fallowrc.jsonc` accept JSON-with-comments syntax (same parser); the `.jsonc` extension lets editors auto-detect JSONC syntax highlighting. Most projects work with zero configuration thanks to 123 auto-detecting framework plugins.
|
|
447
447
|
|
|
448
448
|
```jsonc
|
|
449
449
|
{
|
|
@@ -480,7 +480,7 @@ export const deprecatedHelper = () => {};
|
|
|
480
480
|
## Key Gotchas
|
|
481
481
|
|
|
482
482
|
- **`fix --yes` is required** in non-TTY (agent) environments. Without it, `fix` exits with code 2
|
|
483
|
-
- **Zero config by default.**
|
|
483
|
+
- **Zero config by default.** 123 framework plugins auto-detect, including Wuchale config, Contentlayer content roots, tap and tsd test entry points. Don't create config unless customization is needed
|
|
484
484
|
- **Syntactic analysis only.** No TypeScript compiler, so fully dynamic `import(variable)` is not resolved
|
|
485
485
|
- **Function overloads are deduplicated.** TypeScript function overload signatures are merged into a single export (not reported as separate unused exports)
|
|
486
486
|
- **Re-export chains are resolved.** Exports through barrel files are tracked, not falsely flagged
|
|
@@ -51,7 +51,7 @@ Analyzes the project for unused files, exports, dependencies, types, members, an
|
|
|
51
51
|
| `--top` | `string` | - | Show only the top N items per category |
|
|
52
52
|
| `--file` | `string` | - | Scope output to specific files. Only issues in the specified files are reported. Project-wide dependency issues are suppressed. Warns on non-existent paths. Useful for lint-staged |
|
|
53
53
|
|
|
54
|
-
Common global flags for this command: [`--format`](#global-flags), [`--quiet`](#global-flags), [`--output-file`](#global-flags), [`--
|
|
54
|
+
Common global flags for this command: [`--format`](#global-flags), [`--quiet`](#global-flags), [`--output-file`](#global-flags), [`--changed-since`](#global-flags), [`--max-file-size`](#global-flags), [`--production`](#global-flags), [`--no-production`](#global-flags), [`--production-dead-code`](#global-flags), [`--baseline`](#global-flags), [`--save-baseline`](#global-flags), [`--workspace`](#global-flags), [`--changed-workspaces`](#global-flags), [`--include-entry-exports`](#global-flags).
|
|
55
55
|
<!-- generated:flags:dead-code:end -->
|
|
56
56
|
### Issue Type Filters
|
|
57
57
|
|
|
@@ -398,7 +398,7 @@ Angular templates contribute synthetic `<template>` complexity findings whenever
|
|
|
398
398
|
| `--ownership` | `bool` | `false` | Attach ownership signals to hotspot entries: bus factor (Avelino truck factor), contributor count, top contributor with stale-days, recent contributors (top-3), `suggested_reviewers`, declared CODEOWNERS owner, `ownership_state`, ownership drift, unowned-hotspot detection. Human output gains a project-level summary line. JSON adds `low-bus-factor`, `unowned-hotspot`, `ownership-drift` action types. Test files get a `[test]` tag. Implies `--hotspots`. Requires git. |
|
|
399
399
|
| `--ownership-emails` | `raw\|handle\|anonymized\|hash` | - | Privacy mode for author emails. `handle` shows the local-part only (default, with GitHub noreply unwrap and deterministic same-handle disambiguation). `anonymized` emits stable `xxh3:` pseudonyms; `hash` remains accepted as the legacy spelling. `raw` shows full addresses. Use `anonymized` in regulated environments. Implies `--ownership`. Configure default via `health.ownership.emailMode`. |
|
|
400
400
|
| `--targets` | `bool` | `false` | Show only refactoring targets: ranked recommendations based on complexity, coupling, churn, and dead code signals. Categories: churn+complexity, circular dep, high impact, dead code, complexity, coupling. When no section flags are set, all sections are shown by default. Each target's JSON can include `direct_callers[]` (direct importers with the symbols they import) and `clone_siblings[]` (duplicate-code siblings with stable `dup:<8hex>` fingerprints for `fallow dupes --trace`); both omitted when empty. Human output adds `importers:` / `clones:` lines only when that evidence is present. |
|
|
401
|
-
| `--css` | `bool` | `false` | Add structural CSS analytics: specificity hotspots, !important density, over-complex selectors, deep nesting, and conservative cleanup candidates. Standard CSS is parsed structurally; preprocessor sources are scanned only where fallow can avoid expanding Sass/Less semantics. |
|
|
401
|
+
| `--css` | `bool` | `false` | Add structural CSS analytics: specificity hotspots, !important density, over-complex selectors, deep nesting, and conservative cleanup candidates. Standard CSS is parsed structurally; preprocessor sources are scanned only where fallow can avoid expanding Sass/Less semantics. Also derives `styling_health`, a descriptive A-F grade for CSS quality scored separately from the code `health_score` (never gates); it weights design-token drift (hardcoded value sprawl) over byte-identical repetition. |
|
|
402
402
|
| `--effort` | `low\|medium\|high` | - | Filter refactoring targets by effort level. Implies `--targets`. |
|
|
403
403
|
| `--score` | `bool` | `false` | Show only the project health score (0-100) with letter grade (A/B/C/D/F). The score is included by default when no section flags are set. JSON includes `health_score` object with `score`, `grade`, and `penalties` breakdown. As of v2.55.0, plain `--score` skips the churn-backed hotspot penalty so it does not run a `git log` shell-out per invocation; pass `--hotspots` (or `--targets` with `--score`) to include the hotspot penalty. Snapshot (`--save-snapshot`) and trend (`--trend`) flows still trigger hotspot vital signs so saved data stays complete. |
|
|
404
404
|
| `--min-score` | `string` | - | Fail (exit 1) only when the health score is below this threshold. Implies `--score`. Authoritative CI quality gate: when set, complexity findings are demoted to informational and the exit code is driven solely by the score, so `--min-score 0` always exits 0. Composes with `--min-severity`. |
|
|
@@ -508,7 +508,7 @@ fallow health --format json --quiet --trend
|
|
|
508
508
|
{
|
|
509
509
|
"kind": "health",
|
|
510
510
|
"schema_version": 7,
|
|
511
|
-
"version": "2.
|
|
511
|
+
"version": "2.104.0",
|
|
512
512
|
"elapsed_ms": 32,
|
|
513
513
|
"summary": {
|
|
514
514
|
"files_analyzed": 482,
|
|
@@ -835,6 +835,9 @@ Audits changed files for dead code, complexity, and duplication. Returns a verdi
|
|
|
835
835
|
| `--max-decisions` | `string` | `4` | Cap on the number of consequential structural decisions surfaced in the review brief's decision surface (the working-memory limit). Default 4; clamped to the 3-5 band (4 plus or minus 1). Only consulted on the brief path |
|
|
836
836
|
| `--walkthrough-guide` | `bool` | `false` | Emit the agent-contract WALKTHROUGH GUIDE: the current digest (brief + decision surface), the review direction, the JSON schema the agent must return, and a deterministic graph-snapshot hash pinned into the digest. The digest is built from the graph only (PR prose is never folded in, so it is injection-resistant). Implies the brief; always exits 0. A thin agent skill calls this to fetch the current guide, produces judgment JSON, then reopens with `--walkthrough-file` |
|
|
837
837
|
| `--walkthrough-file` | `string` | - | Ingest an agent's judgment JSON and POST-VALIDATE it against the LIVE graph. Rejects any judgment whose `signal_id` fallow did not emit (anti-hallucination); refuses the whole payload as stale when the echoed graph-snapshot hash no longer matches (the tree moved). The verifier is the graph, not a second model. Implies the brief; always exits 0. The agent's free-text framing is fenced as non-deterministic and never gates or auto-posts |
|
|
838
|
+
| `--walkthrough` | `bool` | `false` | Render the existing walkthrough guide as a staged HUMAN terminal tour (Stage 1 load-bearing / Stage 2 mechanical), or markdown with `--format markdown`. Implies the brief; always exits 0. `--format json --walkthrough` emits the same agent-contract JSON as `--walkthrough-guide` |
|
|
839
|
+
| `--mark-viewed` | `string` | - | Record one or more changed files as VIEWED in the local walkthrough viewed-state ledger (`.fallow/walkthrough-state.json`), then render the tour. Files already viewed (and still current) collapse into the Cleared panel. Repeatable. Stale marks (the tree moved) are ignored on render but never deleted. Only consulted on the `--walkthrough` path |
|
|
840
|
+
| `--show-cleared` | `bool` | `false` | Expand the Cleared panel in the human/markdown walkthrough tour: list each de-prioritized and already-viewed file instead of the collapsed one-line summary. Only consulted on the `--walkthrough` path |
|
|
838
841
|
| `--show-deprioritized` | `bool` | `false` | Expand the de-prioritized units in the review brief's weighted focus map ("show me what you de-prioritized"). The `deprioritized` escape-hatch list is ALWAYS present in `--format json` regardless; this flag only re-expands the collapse-by-default human focus render. Only consulted on the brief path |
|
|
839
842
|
|
|
840
843
|
Common global flags for this command: [`--format`](#global-flags), [`--quiet`](#global-flags), [`--changed-since`](#global-flags), [`--diff-file`](#global-flags), [`--diff-stdin`](#global-flags), [`--workspace`](#global-flags), [`--changed-workspaces`](#global-flags), [`--group-by`](#global-flags), [`--output-file`](#global-flags).
|
|
@@ -900,7 +903,7 @@ fallow audit \
|
|
|
900
903
|
{
|
|
901
904
|
"kind": "audit",
|
|
902
905
|
"schema_version": 7,
|
|
903
|
-
"version": "2.
|
|
906
|
+
"version": "2.104.0",
|
|
904
907
|
"command": "audit",
|
|
905
908
|
"verdict": "fail",
|
|
906
909
|
"changed_files_count": 12,
|
|
@@ -975,7 +978,7 @@ fallow flags --format json --quiet --workspace my-package
|
|
|
975
978
|
```json
|
|
976
979
|
{
|
|
977
980
|
"schema_version": 7,
|
|
978
|
-
"version": "2.
|
|
981
|
+
"version": "2.104.0",
|
|
979
982
|
"elapsed_ms": 116,
|
|
980
983
|
"feature_flags": [],
|
|
981
984
|
"total_flags": 0
|
|
@@ -1076,7 +1079,7 @@ fallow security --gate newly-reachable --changed-since origin/main
|
|
|
1076
1079
|
{
|
|
1077
1080
|
"kind": "security",
|
|
1078
1081
|
"schema_version": "4",
|
|
1079
|
-
"version": "2.
|
|
1082
|
+
"version": "2.104.0",
|
|
1080
1083
|
"elapsed_ms": 42,
|
|
1081
1084
|
"config": {
|
|
1082
1085
|
"rules": {
|
|
@@ -1105,7 +1108,7 @@ fallow security --gate newly-reachable --changed-since origin/main
|
|
|
1105
1108
|
{
|
|
1106
1109
|
"kind": "security",
|
|
1107
1110
|
"schema_version": "4",
|
|
1108
|
-
"version": "2.
|
|
1111
|
+
"version": "2.104.0",
|
|
1109
1112
|
"elapsed_ms": 42,
|
|
1110
1113
|
"config": {
|
|
1111
1114
|
"rules": {
|
|
@@ -1637,7 +1640,6 @@ Available on all commands:
|
|
|
1637
1640
|
| `--group-by` | `owner\|directory\|package\|section` | - | Group output by CODEOWNERS ownership (`owner`), first path component (`directory`), workspace package (`package`, aliases: `workspace`, `pkg`), or GitLab CODEOWNERS `[Section]` headers (`section`, alias: `gl-section`). All output formats partition issues into labeled groups. `section` mode attaches an `owners` array to each group in JSON output |
|
|
1638
1641
|
| `--performance` | `bool` | `false` | Show pipeline timing breakdown |
|
|
1639
1642
|
| `--explain` | `bool` | `false` | JSON: include metric definitions in `_meta`. Human: print a `Description:` line under each section header. Always on for MCP. |
|
|
1640
|
-
| `--legacy-envelope` | `bool` | `false` | Emit the previous typed JSON root envelope without top-level `kind` |
|
|
1641
1643
|
| `--explain-skipped` | `bool` | `false` | Show a per-pattern breakdown for default duplicate ignores |
|
|
1642
1644
|
| `--summary` | `bool` | `false` | Show only category counts without individual items. Useful for dashboards and quick overviews |
|
|
1643
1645
|
| `--ci` | `bool` | `false` | CI mode: `--format sarif --fail-on-issues --quiet` |
|
|
@@ -1813,7 +1815,7 @@ The HTTP layer mirrors the bash `gh_api_retry` / `curl_retry` helpers: `FALLOW_A
|
|
|
1813
1815
|
{
|
|
1814
1816
|
"kind": "dead-code",
|
|
1815
1817
|
"schema_version": 7,
|
|
1816
|
-
"version": "2.
|
|
1818
|
+
"version": "2.104.0",
|
|
1817
1819
|
"elapsed_ms": 45,
|
|
1818
1820
|
"total_issues": 12,
|
|
1819
1821
|
"entry_points": {
|
|
@@ -1973,7 +1975,7 @@ When `--baseline` is used in combined output, the JSON includes a `baseline_delt
|
|
|
1973
1975
|
{
|
|
1974
1976
|
"kind": "dupes",
|
|
1975
1977
|
"schema_version": 7,
|
|
1976
|
-
"version": "2.
|
|
1978
|
+
"version": "2.104.0",
|
|
1977
1979
|
"elapsed_ms": 82,
|
|
1978
1980
|
"total_clones": 15,
|
|
1979
1981
|
"total_lines_duplicated": 230,
|
|
@@ -2017,11 +2019,11 @@ When running `fallow` with no subcommand (all analyses), the JSON output combine
|
|
|
2017
2019
|
{
|
|
2018
2020
|
"kind": "combined",
|
|
2019
2021
|
"schema_version": 7,
|
|
2020
|
-
"version": "2.
|
|
2022
|
+
"version": "2.104.0",
|
|
2021
2023
|
"elapsed_ms": 159,
|
|
2022
2024
|
"check": {
|
|
2023
2025
|
"schema_version": 7,
|
|
2024
|
-
"version": "2.
|
|
2026
|
+
"version": "2.104.0",
|
|
2025
2027
|
"elapsed_ms": 45,
|
|
2026
2028
|
"total_issues": 12,
|
|
2027
2029
|
"unused_files": [],
|
|
@@ -2229,6 +2231,7 @@ preset = "bulletproof"
|
|
|
2229
2231
|
- `cache.maxSizeMb`: cap the serialized extraction cache size in megabytes. `FALLOW_CACHE_MAX_SIZE` wins over this config field
|
|
2230
2232
|
- `usedClassMembers`: class method/property names that extend the built-in Angular/React lifecycle allowlist with framework-invoked names. Each entry is a plain string (global suppression) or a scoped object `{ extends?, implements?, members }` matching only classes with the given heritage. Strings can be exact names (`"agInit"`) or glob patterns (`"*"` matches every member, `"enter*"` prefix, `"*Handler"` suffix, `"on*Event"` combined). Use scoped rules for common names like `refresh` or `execute` to avoid false negatives on unrelated classes; global strings for unique names like `agInit`. Example: `["agInit", { "implements": "ICellRendererAngularComp", "members": ["refresh"] }, { "extends": "BaseCommand", "members": ["execute"] }, { "extends": "GrammarBaseListener", "members": ["enter*", "exit*"] }]`. Glob patterns that match zero members emit a `WARN` so dead allowlist entries surface. An unconstrained scoped rule (no `extends` or `implements`) is rejected at load time. Use plugin-level `usedClassMembers` in a `.fallow/plugins/*.jsonc` file for library-specific allowlists
|
|
2231
2233
|
- `resolve.conditions`: additional package.json `exports` / `imports` condition names to honor during module resolution. Baseline conditions (`development`, `import`, `require`, `default`, `types`, `node`, plus `react-native` / `browser` under RN/Expo) are always included; user entries prepend ahead of them. Use for community conditions like `worker`, `edge-light`, `deno`, or custom bundler conditions. Example: `{ "resolve": { "conditions": ["worker", "edge-light"] } }`
|
|
2234
|
+
- `unusedComponentProps.ignorePattern`: opt-in regex that exempts a component prop from `unused-component-props` when the prop's LOCAL destructure binding name matches (the leading-underscore "accepted-but-intentionally-unused" convention, mirroring TS `noUnusedParameters` + ESLint `varsIgnorePattern` / `argsIgnorePattern`). Applies to Vue, Svelte, Astro, and React/Preact props. The match is on the local alias (`_stage` in `let { stage: _stage } = $props()`), not the public prop name the finding reports (`stage`); matching is unanchored like ESLint's `RegExp.test`, so anchor with `^_`. An invalid regex fails config load. Example: `{ "unusedComponentProps": { "ignorePattern": "^_" } }`
|
|
2232
2235
|
|
|
2233
2236
|
---
|
|
2234
2237
|
|
|
@@ -23,7 +23,7 @@ Always preview with `--dry-run` before applying. This is a destructive operation
|
|
|
23
23
|
|
|
24
24
|
## Don't Create Config Unless Needed
|
|
25
25
|
|
|
26
|
-
Fallow works with zero configuration for most projects thanks to
|
|
26
|
+
Fallow works with zero configuration for most projects thanks to 123 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
|
|
@@ -484,10 +484,16 @@ When `fallow health --css` reports `css_analytics.unused_theme_tokens`, treat th
|
|
|
484
484
|
|
|
485
485
|
The detector intentionally abstains when a Tailwind plugin or published CSS surface could consume tokens invisibly. Always run the row's `actions[].command` verification before deleting a token, and do not run `fallow fix` for these rows.
|
|
486
486
|
|
|
487
|
+
The same Tailwind v4 projects also get `css_analytics.token_consumers`, the reverse index: per `@theme` token, where it is consumed (a `consumer_count` plus a located `consumers[]` sample tagged `theme-var` / `css-var` / `utility` / `apply`), so you can read a token's blast radius before changing it. Treat `consumer_count` as a static lower bound: a computed class name such as `bg-${color}` is invisible to the scan, so a `0` here is the same "nothing fallow can see consumes this" population as `unused_theme_tokens`, not a deletion proof. `token_consumers` is descriptive context with no `actions[]`; drive any deletion off `unused_theme_tokens` and its verification command.
|
|
488
|
+
|
|
489
|
+
`token_consumers` also covers CSS-in-JS token DEFINITIONS (StyleX `defineVars`, vanilla-extract `createTheme` / `createThemeContract` / `createGlobalTheme`), disambiguated by the consumer `kind`: a CSS-in-JS entry has `kind` `js-member`, `token` is the binding-qualified dotted access path (`vars.color.primary`), and `namespace` is the defining binding (`vars`). The cross-file scan resolves only relative imports, so a token consumed through a path alias (`@/tokens`) or a bare-package import is not counted (a sharper lower bound), and unlike Tailwind there is no corroborating dead-token finding, so a CSS-in-JS `consumer_count` of `0` is a weaker signal. Gated on a declared CSS-in-JS library (`@stylexjs/stylex` / `@vanilla-extract/css`); Panda (`defineTokens` / `token('...')`) is a planned follow-on.
|
|
490
|
+
|
|
487
491
|
## CSS Health Candidates Are Advisory
|
|
488
492
|
|
|
489
493
|
`fallow health --css` also emits advisory cleanup and typo candidates in `css_analytics.unreferenced_css_classes` (a plain-CSS class defined but matched by no `class`/`className` in project markup), `css_analytics.unresolved_class_references` (the reverse: a markup class one edit away from a defined class, a likely typo), `css_analytics.unused_font_faces`, `css_analytics.undefined_keyframes`, and `css_analytics.font_size_unit_mix`. Treat them like review prompts, not confirmed defects. Run each row's `actions[].command` before changing CSS, because fonts, classes, animations, and type scales can be driven by inline styles, JavaScript, CMS templates, or preprocessor expansion that static analysis intentionally does not execute.
|
|
490
494
|
|
|
495
|
+
`fallow health --css` also derives `styling_health`, a descriptive A-F grade (and 0-100 `score`) for CSS quality, scored SEPARATELY from the code `health_score`. It is descriptive-only: it never gates an exit code, badge, or CI, and never affects the code score, so do NOT branch automation on it. Read it as a design-system credibility signal. It weights design-token DRIFT over byte-identical repetition: the `token_erosion` penalty includes a hardcoded-value-sprawl term over distinct `box-shadow` / `border-radius` / `line-height` values, but counts only HARDCODED literals: a system that references its scale via `var(--*)` scores 0 sprawl regardless of how many tokens it defines (so "tokenize repeated values" is the remedy the human output suggests). A grade from a thin CSS surface (or predominantly compile-time-atomic CSS-in-JS like StyleX/Panda) is marked `confidence: "low"` with a reason. `styling_health.formula_version` bumps when the rubric is recalibrated; if you diff the score/grade over time, gate on it and re-baseline at a bump rather than reading the step-change as a regression.
|
|
496
|
+
|
|
491
497
|
---
|
|
492
498
|
|
|
493
499
|
## Library Packages: Use `publicPackages` Instead of Visibility Tags
|
|
@@ -640,7 +640,7 @@ Focus on findings that are BOTH dead code and duplicated:
|
|
|
640
640
|
|
|
641
641
|
## Custom Plugin Setup
|
|
642
642
|
|
|
643
|
-
For frameworks not covered by the
|
|
643
|
+
For frameworks not covered by the 123 built-in plugins.
|
|
644
644
|
|
|
645
645
|
### Option 1: Inline framework config
|
|
646
646
|
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
|
|
26
26
|
|
|
27
27
|
/**
|
|
28
|
-
* Schemas for the JSON output of fallow commands. Object-shaped envelopes covered by the `FallowOutput` contract carry a top-level `kind` discriminator (for example `dead-code`, `dead-code-grouped`, `health`, `dupes`, `combined`, `audit`, `explain`, `inspect_target`, `impact`, `security`, `coverage-setup`, `coverage-analyze`, `list-boundaries`, `review-envelope`, and `review-reconcile`). Consumers should branch on `kind` instead of probing for unique field presence.
|
|
28
|
+
* Schemas for the JSON output of fallow commands. Object-shaped envelopes covered by the `FallowOutput` contract carry a top-level `kind` discriminator (for example `dead-code`, `dead-code-grouped`, `health`, `dupes`, `combined`, `audit`, `explain`, `inspect_target`, `impact`, `security`, `coverage-setup`, `coverage-analyze`, `list-boundaries`, `review-envelope`, and `review-reconcile`). Consumers should branch on `kind` instead of probing for unique field presence. `CodeClimateOutput` is a bare JSON array (per the Code Climate / GitLab Code Quality spec) and stays a sibling root branch discriminated by checking whether the document root is an array.
|
|
29
29
|
*/
|
|
30
30
|
export type FallowJsonOutput = (FallowOutput | CodeClimateOutput)
|
|
31
31
|
/**
|
|
@@ -34,11 +34,9 @@ export type FallowJsonOutput = (FallowOutput | CodeClimateOutput)
|
|
|
34
34
|
* schema derived from this enum drives the document-root `oneOf` in
|
|
35
35
|
* `docs/output-schema.json`.
|
|
36
36
|
*
|
|
37
|
-
* The
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
* compatibility flag that removes only this document-root `kind` field from
|
|
41
|
-
* CLI JSON output; nested report objects are not rewritten.
|
|
37
|
+
* The wire shape carries a top-level `kind` discriminator so agents and
|
|
38
|
+
* schema-validating clients can select the variant in O(1) instead of probing
|
|
39
|
+
* for unique field presence.
|
|
42
40
|
*
|
|
43
41
|
* One envelope is intentionally NOT in this enum:
|
|
44
42
|
* - `CodeClimateOutput` serializes as a bare JSON array
|
|
@@ -601,6 +599,24 @@ export type CssCandidateActionType = ("verify-unused" | "verify-undefined" | "co
|
|
|
601
599
|
* Discriminant for [`UnusedAtRule::kind`].
|
|
602
600
|
*/
|
|
603
601
|
export type UnusedAtRuleKind = ("property-registration" | "layer")
|
|
602
|
+
/**
|
|
603
|
+
* The surface through which a design token is consumed. The `theme-var` /
|
|
604
|
+
* `css-var` / `utility` / `apply` kinds are Tailwind v4 `@theme` consumption; the
|
|
605
|
+
* `js-member` kind is CSS-in-JS consumption (a cross-module member access on an
|
|
606
|
+
* imported StyleX/vanilla-extract token binding). The kind is the disjoint origin
|
|
607
|
+
* signal that distinguishes a Tailwind token entry from a CSS-in-JS token entry in
|
|
608
|
+
* the shared `token_consumers` list.
|
|
609
|
+
*/
|
|
610
|
+
export type ConsumerKind = ("theme-var" | "css-var" | "utility" | "apply" | "js-member")
|
|
611
|
+
/**
|
|
612
|
+
* Trust level for a [`StylingHealth`] grade. TWO variants (not the three-tier
|
|
613
|
+
* `high`/`medium`/`low` of [`crate::Confidence`] / `FeatureFlagConfidence`) ON
|
|
614
|
+
* PURPOSE: styling confidence is binary (the grade is either reliable for the
|
|
615
|
+
* analyzed surface or it is not), not three distinct evidence tiers, so a
|
|
616
|
+
* never-emitted `Medium` would be dead surface. Serializes lowercase (`"high"` /
|
|
617
|
+
* `"low"`), matching the sibling confidence enums' vocabulary.
|
|
618
|
+
*/
|
|
619
|
+
export type StylingHealthConfidence = ("high" | "low")
|
|
604
620
|
export type InspectTargetDescriptor = ({
|
|
605
621
|
file: string
|
|
606
622
|
type: "file"
|
|
@@ -788,12 +804,14 @@ export type RiskClass = ("low" | "medium" | "high")
|
|
|
788
804
|
*/
|
|
789
805
|
export type ReviewEffort = ("glance" | "review" | "deep_dive")
|
|
790
806
|
/**
|
|
791
|
-
* The focus label for a review unit.
|
|
792
|
-
*
|
|
793
|
-
*
|
|
794
|
-
*
|
|
807
|
+
* The focus label for a review unit. `Skip` is the SAFE explicit-skip label and
|
|
808
|
+
* is runtime-backed ONLY: it is producible solely on the paid runtime path
|
|
809
|
+
* and solely for a unit runtime-proves cold with zero risk signals. Free mode
|
|
810
|
+
* (no runtime evidence) emits only `ReviewHere` / `NotPrioritized`, never `Skip`
|
|
811
|
+
* (the build-focus-map labeller cannot reach the `Skip` arm without runtime
|
|
812
|
+
* input), so the free-tier "rank but never skip" stance holds by construction.
|
|
795
813
|
*/
|
|
796
|
-
export type FocusLabel = ("review-here" | "not-prioritized")
|
|
814
|
+
export type FocusLabel = ("review-here" | "not-prioritized" | "skip")
|
|
797
815
|
/**
|
|
798
816
|
* A per-unit confidence flag. The EXACT panel-decided strings: a dynamically-
|
|
799
817
|
* wired or re-export-heavy unit carries one so its static-reachability signal is
|
|
@@ -4017,6 +4035,14 @@ framework_health?: (FrameworkHealthDiagnostics | null)
|
|
|
4017
4035
|
* over-complex selectors, deep nesting). Present only with `--css`.
|
|
4018
4036
|
*/
|
|
4019
4037
|
css_analytics?: (CssAnalyticsReport | null)
|
|
4038
|
+
/**
|
|
4039
|
+
* Styling-health score and letter grade: a SECOND health axis derived from
|
|
4040
|
+
* the CSS analytics (the design-system axis), orthogonal to the JS/TS code
|
|
4041
|
+
* `health_score`. Present only with `--css` (the same condition as
|
|
4042
|
+
* `css_analytics`), so a plain `fallow health` run is byte-unchanged. The
|
|
4043
|
+
* code score is never affected by this field.
|
|
4044
|
+
*/
|
|
4045
|
+
styling_health?: (StylingHealth | null)
|
|
4020
4046
|
}
|
|
4021
4047
|
/**
|
|
4022
4048
|
* Wire envelope for a single complexity finding.
|
|
@@ -5882,6 +5908,18 @@ unused_font_faces?: UnusedFontFace[]
|
|
|
5882
5908
|
* downstream repo. Sorted by `(path, line, token)`.
|
|
5883
5909
|
*/
|
|
5884
5910
|
unused_theme_tokens?: UnusedThemeToken[]
|
|
5911
|
+
/**
|
|
5912
|
+
* A location-aware reverse index of Tailwind v4 `@theme` token consumers:
|
|
5913
|
+
* per token, where it is consumed (`var()` reads, `@apply` bodies, generated
|
|
5914
|
+
* utility classes) and through which surface, plus the full `consumer_count`
|
|
5915
|
+
* (a static lower bound) and the defining site. Built from the same gated
|
|
5916
|
+
* candidate set as `unused_theme_tokens` (v4 + non-plugin + non-published +
|
|
5917
|
+
* whole-scope), so a token with `consumer_count: 0` is the same "nothing
|
|
5918
|
+
* consumes this" signal. Sorted by token; empty when the project is not
|
|
5919
|
+
* Tailwind v4 or a plugin / published-library / partial-scope run gated the
|
|
5920
|
+
* scan out.
|
|
5921
|
+
*/
|
|
5922
|
+
token_consumers?: TokenConsumers[]
|
|
5885
5923
|
/**
|
|
5886
5924
|
* The project authors `font-size` values in several units (`px`, `rem`,
|
|
5887
5925
|
* `em`, `%`), with a per-unit distinct-value count: a type-scale
|
|
@@ -6068,11 +6106,16 @@ nesting_depth: number
|
|
|
6068
6106
|
/**
|
|
6069
6107
|
* Project-wide CSS analytics aggregates across every analyzed stylesheet
|
|
6070
6108
|
* (including stylesheets with no notable rule, which are not listed
|
|
6071
|
-
* individually).
|
|
6109
|
+
* individually in `files`).
|
|
6072
6110
|
*/
|
|
6073
6111
|
export interface CssAnalyticsSummary {
|
|
6074
6112
|
/**
|
|
6075
|
-
* Stylesheets analyzed
|
|
6113
|
+
* Stylesheets analyzed: standard `.css` files, Vue/Svelte SFC `<style>`
|
|
6114
|
+
* blocks, and (dep-gated) CSS-in-JS, both the tagged-template form and the
|
|
6115
|
+
* object form (`style({...})` / `stylex.create({...})` / `css({...})`). SCSS
|
|
6116
|
+
* is skipped. Note: flat atomic object CSS-in-JS (StyleX/Panda) is counted
|
|
6117
|
+
* here and contributes to these aggregates, but has no notable rules, so its
|
|
6118
|
+
* files never appear in the per-file `files` list.
|
|
6076
6119
|
*/
|
|
6077
6120
|
files_analyzed: number
|
|
6078
6121
|
/**
|
|
@@ -6526,6 +6569,92 @@ line: number
|
|
|
6526
6569
|
*/
|
|
6527
6570
|
actions: CssCandidateAction[]
|
|
6528
6571
|
}
|
|
6572
|
+
/**
|
|
6573
|
+
* A location-aware reverse index of where one design token is consumed, so an
|
|
6574
|
+
* agent editing the token can see its blast radius before changing or removing
|
|
6575
|
+
* it. Covers TWO token origins. The always-available discriminator is the `token`
|
|
6576
|
+
* SHAPE: a Tailwind token is the `--`-prefixed custom property (`--color-brand`),
|
|
6577
|
+
* a CSS-in-JS token is a dotted access path with no `--` prefix
|
|
6578
|
+
* (`vars.color.primary`). The per-consumer `kind` also discriminates origin, but
|
|
6579
|
+
* only when `consumer_count > 0` (a `consumer_count: 0` entry has an empty
|
|
6580
|
+
* `consumers` array and thus no `kind`), so branch on the `token` prefix for the
|
|
6581
|
+
* zero-consumer case. The two origins:
|
|
6582
|
+
*
|
|
6583
|
+
* - Tailwind v4 `@theme` tokens (kinds `theme-var` / `css-var` / `utility` /
|
|
6584
|
+
* `apply`), built from the same gated candidate set as `unused_theme_tokens`
|
|
6585
|
+
* (v4 + non-plugin + non-published + whole-scope), so a `consumer_count: 0`
|
|
6586
|
+
* corroborates the `unused_theme_tokens` "nothing consumes this" finding.
|
|
6587
|
+
* - CSS-in-JS tokens (kind `js-member`) from StyleX `defineVars` /
|
|
6588
|
+
* vanilla-extract `createTheme` family definitions, consumed via cross-module
|
|
6589
|
+
* member access. NOTE: CSS-in-JS has NO corroborating dead-token finding (there
|
|
6590
|
+
* is no `unused_theme_tokens` analogue), so a CSS-in-JS `consumer_count: 0` is a
|
|
6591
|
+
* weaker signal than the Tailwind case (and the cross-file scan is relative-import
|
|
6592
|
+
* only, so alias / bare-package imports are not counted).
|
|
6593
|
+
*
|
|
6594
|
+
* This is DESCRIPTIVE context (a blast-radius lookup), not a finding, so it
|
|
6595
|
+
* deliberately carries no `actions` array (unlike the cleanup-candidate types in
|
|
6596
|
+
* this module). `consumer_count` is always a STATIC lower bound (a computed class
|
|
6597
|
+
* name like `bg-${color}`, or a CSS-in-JS access through an unresolved alias
|
|
6598
|
+
* import, is not counted).
|
|
6599
|
+
*/
|
|
6600
|
+
export interface TokenConsumers {
|
|
6601
|
+
/**
|
|
6602
|
+
* The token identity. For a Tailwind `@theme` token this is the full custom
|
|
6603
|
+
* property as authored, INCLUDING the `--` prefix (`--color-brand`). For a
|
|
6604
|
+
* CSS-in-JS token (kind `js-member`) this is the binding-qualified dotted
|
|
6605
|
+
* access path, NO `--` prefix (`vars.color.primary`), matching how consumers
|
|
6606
|
+
* read it. The presence of the `--` prefix distinguishes the two origins.
|
|
6607
|
+
*/
|
|
6608
|
+
token: string
|
|
6609
|
+
/**
|
|
6610
|
+
* For a Tailwind token, the v4 theme namespace (`color`, `radius`,
|
|
6611
|
+
* `font-weight`, ...). For a CSS-in-JS token (kind `js-member`), the defining
|
|
6612
|
+
* export BINDING the token set is accessed through (`vars`), which identifies
|
|
6613
|
+
* the token set, NOT a semantic group. (The field is thus overloaded by
|
|
6614
|
+
* origin; branch on `consumers[].kind` or the `token` shape.)
|
|
6615
|
+
*/
|
|
6616
|
+
namespace: string
|
|
6617
|
+
/**
|
|
6618
|
+
* Project-root-relative, forward-slash path to the declaring stylesheet
|
|
6619
|
+
* (Tailwind) or the JS/TS token-definition file (CSS-in-JS).
|
|
6620
|
+
*/
|
|
6621
|
+
definition_path: string
|
|
6622
|
+
/**
|
|
6623
|
+
* 1-based line of the token's definition (inside the `@theme` block for
|
|
6624
|
+
* Tailwind; the token key inside the `defineVars`/`createTheme` object for
|
|
6625
|
+
* CSS-in-JS).
|
|
6626
|
+
*/
|
|
6627
|
+
definition_line: number
|
|
6628
|
+
/**
|
|
6629
|
+
* The FULL number of consumer locations found, a STATIC LOWER BOUND: a
|
|
6630
|
+
* computed class name (`bg-${color}`) or a value read outside CSS/markup the
|
|
6631
|
+
* scan never sees is not counted. This is the aggregate over every consumer,
|
|
6632
|
+
* computed BEFORE [`consumers`](Self::consumers) is capped to a sample.
|
|
6633
|
+
*/
|
|
6634
|
+
consumer_count: number
|
|
6635
|
+
/**
|
|
6636
|
+
* A capped, deterministically-sorted sample of consumer locations (at most
|
|
6637
|
+
* [`TOKEN_CONSUMER_SAMPLE_CAP`]). The full count lives in
|
|
6638
|
+
* [`consumer_count`](Self::consumer_count); use this list to jump to
|
|
6639
|
+
* representative consumers, not to enumerate every one.
|
|
6640
|
+
*/
|
|
6641
|
+
consumers: TokenConsumerLocation[]
|
|
6642
|
+
}
|
|
6643
|
+
/**
|
|
6644
|
+
* Where one Tailwind v4 `@theme` token is consumed, and through which surface.
|
|
6645
|
+
* One entry in a [`TokenConsumers::consumers`] sample.
|
|
6646
|
+
*/
|
|
6647
|
+
export interface TokenConsumerLocation {
|
|
6648
|
+
/**
|
|
6649
|
+
* Project-root-relative, forward-slash path to the consuming file.
|
|
6650
|
+
*/
|
|
6651
|
+
path: string
|
|
6652
|
+
/**
|
|
6653
|
+
* 1-based line of the consuming reference in that file.
|
|
6654
|
+
*/
|
|
6655
|
+
line: number
|
|
6656
|
+
kind: ConsumerKind
|
|
6657
|
+
}
|
|
6529
6658
|
/**
|
|
6530
6659
|
* A design-token notation-consistency candidate: the distinct notations used
|
|
6531
6660
|
* across the codebase for one value axis (today, length units on `font-size`),
|
|
@@ -6564,6 +6693,75 @@ notation: string
|
|
|
6564
6693
|
*/
|
|
6565
6694
|
count: number
|
|
6566
6695
|
}
|
|
6696
|
+
/**
|
|
6697
|
+
* Project-level styling-health score: a SECOND health axis computed purely from
|
|
6698
|
+
* the structural CSS analytics (`CssAnalyticsReport`), orthogonal to the JS/TS
|
|
6699
|
+
* code-health [`HealthScore`]. Surfaced only alongside the `--css` analytics, so
|
|
6700
|
+
* a plain `fallow health` run is byte-unchanged. The code score and grade stay
|
|
6701
|
+
* untouched: styling health is additive, never folded into the code score.
|
|
6702
|
+
*
|
|
6703
|
+
* Like [`HealthScore`], the score starts at 100 and subtracts capped per-category
|
|
6704
|
+
* penalties; the grade reuses the shared [`letter_grade`] thresholds verbatim
|
|
6705
|
+
* (A>=85, B>=70, C>=55, D>=40, F<40), so the two axes are read on one scale.
|
|
6706
|
+
*/
|
|
6707
|
+
export interface StylingHealth {
|
|
6708
|
+
formula_version: number
|
|
6709
|
+
score: number
|
|
6710
|
+
grade: string
|
|
6711
|
+
penalties: StylingHealthPenalties
|
|
6712
|
+
confidence: StylingHealthConfidence
|
|
6713
|
+
/**
|
|
6714
|
+
* Human-readable reason the grade is low-confidence: either the declaration
|
|
6715
|
+
* and stylesheet counts a thin grade was computed from, or that structure is
|
|
6716
|
+
* not assessable for compile-time-atomic CSS-in-JS. `None` when confidence is
|
|
6717
|
+
* `High`. Prose, not a stable machine field: gate on `confidence`, not on
|
|
6718
|
+
* this string.
|
|
6719
|
+
*/
|
|
6720
|
+
confidence_reason?: (string | null)
|
|
6721
|
+
}
|
|
6722
|
+
/**
|
|
6723
|
+
* Per-category penalty breakdown for the styling-health score. Each field is the
|
|
6724
|
+
* number of points subtracted from a starting 100 for one CSS signal family,
|
|
6725
|
+
* already capped at its category ceiling. A `0.0` field means "the signal was
|
|
6726
|
+
* evaluated and clean"; the whole struct is only ever built when CSS analytics
|
|
6727
|
+
* were produced, so there is no "missing pipeline" ambiguity to model with
|
|
6728
|
+
* `Option` here (the parent `StylingHealth` is itself `Option` on the report).
|
|
6729
|
+
*/
|
|
6730
|
+
export interface StylingHealthPenalties {
|
|
6731
|
+
/**
|
|
6732
|
+
* Copy-paste declaration blocks (`duplicate_declaration_blocks`), scaled by
|
|
6733
|
+
* total removable declarations. Capped at 20pt.
|
|
6734
|
+
*/
|
|
6735
|
+
duplication: number
|
|
6736
|
+
/**
|
|
6737
|
+
* Dead styling surface, two independently-normalized terms summed and capped
|
|
6738
|
+
* at 20pt: (a) unused `@theme` tokens as a share of the total `@theme` token
|
|
6739
|
+
* population (size-independent, so a declaration-sparse Tailwind project is
|
|
6740
|
+
* not penalized for a few dead tokens); plus (b) the other dead entities
|
|
6741
|
+
* (unreferenced classes, unused `@property`/`@layer` at-rules, dead
|
|
6742
|
+
* `@font-face` families) as a share of `total_declarations`.
|
|
6743
|
+
*/
|
|
6744
|
+
dead_surface: number
|
|
6745
|
+
/**
|
|
6746
|
+
* Broken references: markup classes one edit from a defined class
|
|
6747
|
+
* (`unresolved_class_references`) and animations referencing a `@keyframes`
|
|
6748
|
+
* defined nowhere (`undefined_keyframes`). Capped at 15pt.
|
|
6749
|
+
*/
|
|
6750
|
+
broken_references: number
|
|
6751
|
+
/**
|
|
6752
|
+
* Design-token erosion: mixed `font-size` units (`font_size_unit_mix`),
|
|
6753
|
+
* Tailwind arbitrary-value bypasses (`tailwind_arbitrary_values`), and
|
|
6754
|
+
* distinct HARDCODED `box-shadow`/`border-radius`/`line-height` values above
|
|
6755
|
+
* per-axis baselines (the v3 value-sprawl drift sub-term; `var(--*)`-
|
|
6756
|
+
* referenced values are not counted). Capped at 10pt.
|
|
6757
|
+
*/
|
|
6758
|
+
token_erosion: number
|
|
6759
|
+
/**
|
|
6760
|
+
* Structural smells from the summary aggregates: `!important` density and
|
|
6761
|
+
* deep style-rule nesting. Capped at 10pt.
|
|
6762
|
+
*/
|
|
6763
|
+
structural: number
|
|
6764
|
+
}
|
|
6567
6765
|
/**
|
|
6568
6766
|
* Envelope emitted by `fallow explain <issue-type> --format json`.
|
|
6569
6767
|
*
|
|
@@ -6991,6 +7189,14 @@ framework_health?: (FrameworkHealthDiagnostics | null)
|
|
|
6991
7189
|
* over-complex selectors, deep nesting). Present only with `--css`.
|
|
6992
7190
|
*/
|
|
6993
7191
|
css_analytics?: (CssAnalyticsReport | null)
|
|
7192
|
+
/**
|
|
7193
|
+
* Styling-health score and letter grade: a SECOND health axis derived from
|
|
7194
|
+
* the CSS analytics (the design-system axis), orthogonal to the JS/TS code
|
|
7195
|
+
* `health_score`. Present only with `--css` (the same condition as
|
|
7196
|
+
* `css_analytics`), so a plain `fallow health` run is byte-unchanged. The
|
|
7197
|
+
* code score is never affected by this field.
|
|
7198
|
+
*/
|
|
7199
|
+
styling_health?: (StylingHealth | null)
|
|
6994
7200
|
grouped_by?: (GroupByMode | null)
|
|
6995
7201
|
groups?: (HealthGroup[] | null)
|
|
6996
7202
|
_meta?: (Meta | null)
|
|
@@ -8778,10 +8984,11 @@ export interface FocusMap {
|
|
|
8778
8984
|
*/
|
|
8779
8985
|
review_here: FocusUnit[]
|
|
8780
8986
|
/**
|
|
8781
|
-
* EVERY
|
|
8987
|
+
* EVERY de-prioritized unit (`not-prioritized`, plus runtime-backed `skip`
|
|
8988
|
+
* units on the paid path) -- the escape hatch. Always present and fully
|
|
8782
8989
|
* enumerated so a reviewer can always "show me what you de-prioritized"; the
|
|
8783
8990
|
* human brief collapses it by default and re-expands under
|
|
8784
|
-
* `--show-deprioritized`.
|
|
8991
|
+
* `--show-deprioritized`. Nothing is ever hidden, including a `skip`.
|
|
8785
8992
|
*/
|
|
8786
8993
|
deprioritized: FocusUnit[]
|
|
8787
8994
|
}
|
|
@@ -8806,9 +9013,9 @@ reason: string
|
|
|
8806
9013
|
confidence?: ConfidenceFlag[]
|
|
8807
9014
|
}
|
|
8808
9015
|
/**
|
|
8809
|
-
* The composite attention score, with the
|
|
8810
|
-
*
|
|
8811
|
-
*
|
|
9016
|
+
* The composite attention score, with the deterministic component sub-scores
|
|
9017
|
+
* kept on the wire so the runtime layer adds its weight without recomputing the
|
|
9018
|
+
* signals.
|
|
8812
9019
|
*/
|
|
8813
9020
|
export interface FocusScore {
|
|
8814
9021
|
/**
|
|
@@ -8829,7 +9036,16 @@ risk_zone: number
|
|
|
8829
9036
|
*/
|
|
8830
9037
|
change_shape: number
|
|
8831
9038
|
/**
|
|
8832
|
-
*
|
|
9039
|
+
* Runtime-weight component (paid): a hot path (runtime evidence of high
|
|
9040
|
+
* invocation) adds an invocation-bucketed weight so it amplifies the blast
|
|
9041
|
+
* and outranks an otherwise-equal cold unit. `0` in free mode (no runtime
|
|
9042
|
+
* input), so the free-tier total stays the four deterministic components and
|
|
9043
|
+
* is byte-identical to the no-runtime baseline.
|
|
9044
|
+
*/
|
|
9045
|
+
runtime?: number
|
|
9046
|
+
/**
|
|
9047
|
+
* The summed total of every present component (the four deterministic ones
|
|
9048
|
+
* plus the runtime weight).
|
|
8833
9049
|
*/
|
|
8834
9050
|
total: number
|
|
8835
9051
|
}
|