Lint Sync
Local writes only, and only two of the three modes write. sync and audit analyse and
report — no config file changes unless the user asks for the generated patch to be applied.
update rewrites the bundled references/*.json files in this skill. Nothing is staged,
committed, or pushed, and no remote service is written to.
Edit mechanic. A config file that does not exist yet — .oxlintrc.json, .oxfmtrc.json —
is generated whole from references/oxc-defaults.json. Every file that already exists —
vite.config.ts, biome.json, an ESLint config, a reference JSON in update mode — is changed
with targeted edits per hunk, never a whole-file rewrite: a formatter-wide reflow of someone's
vite.config.ts buries the rules that actually changed.
Purpose
Two workflows in one skill:
- Migrate — Move ESLint/Prettier rules to Biome or Oxc with rule-by-rule comparison
- Modernize — Update existing Biome/Oxc configs: promote nursery rules, enable new quality flags, apply latest formatter options
Modes
| Mode | Trigger | Description |
|---|---|---|
| sync | No mode or sync |
Quick overlap check (migrate) or nursery/feature check (modernize). |
| audit | audit |
Full analysis with performance data, coverage stats, migration/modernization checklist. |
| update | update |
Refresh mapping and version reference files from upstream sources. |
Asking the User
Every question in this skill is written as AskUserQuestion options. Use that tool where
the host offers it, or the host's nearest structured-choice equivalent. Where the host has
neither, ask the same question in normal chat as a numbered list of 2–5 options —
recommended first, one short line of description each — and wait for the user to reply
with a number.
Workflow
Step 1: Detect All Configs
Find all relevant configuration files:
# Source tools (ESLint / Prettier)
fd -t f '(eslint\.config\.(js|ts|mjs|cjs)|\.eslintrc\..*)' --max-depth 2
fd -t f '(\.prettierrc(\.(js|cjs|mjs|json|yaml|yml|toml))?|prettier\.config\.(js|cjs|mjs|ts))' --max-depth 2
# Target tools (Biome / Oxc)
fd -t f 'biome\.json(c)?$' --max-depth 2
fd -t f '(\.oxlintrc\.(json|jsonc)|oxlint\.config\.(ts|js))' --max-depth 2
fd -t f '(\.oxfmtrc\.(json|jsonc)|oxfmt\.config\.(ts|js))' --max-depth 2
# Vite+ (unified Oxc config)
fd -t f 'vite\.config\.(ts|js|mjs)$' --max-depth 2
Vite+ detection: If vite.config.ts exists, check if it imports from 'vite-plus'. Also check package.json for vite-plus in dependencies. If Vite+ is in use, its lint and fmt blocks in vite.config.ts replace standalone .oxlintrc.json and .oxfmtrc.json. Set $VITEPLUS=true and treat Oxc config as found.
# Quick Vite+ detection
grep -l "from ['\"]vite-plus['\"]" vite.config.ts 2>/dev/null
Run every detection command before deciding anything, and end the phase with one line naming
what was found: Found: eslint.config.ts, .prettierrc, vite.config.ts (Vite+) · no biome.json.
Step 2: Determine Workflow
| ESLint/Prettier | Biome | Oxc/Vite+ | Workflow |
|---|---|---|---|
| Found | Found | - | Migrate to Biome |
| Found | - | Found | Migrate to Oxc |
| Found | Found | Found | Ask user which target |
| Found | - | - | Ask user which target to migrate to |
| - | Found | - | Modernize Biome |
| - | - | Found | Modernize Oxc |
| - | Found | Found | Modernize both (ask which first) |
| - | - | - | Nothing to do — inform user |
Vite+ counts as Oxc in the table above. When $VITEPLUS=true, all Oxc config reads/writes target the lint and fmt blocks in vite.config.ts instead of standalone config files.
Ask the target choice per Asking the User:
Which tool would you like to migrate to?
1. Biome (Recommended) — All-in-one linter + formatter, single config file
2. Oxc (Oxlint + Oxfmt) — Separate linter and formatter, ESLint-compatible rule names
3. Skip — Don't migrate, just show the analysis
Migration Workflow
Runs when ESLint/Prettier configs are present.
M1: Get Tool Versions
pnpm exec eslint --version 2>/dev/null || npx eslint --version 2>/dev/null
# Target-specific
pnpm exec biome --version 2>/dev/null || npx biome --version 2>/dev/null
# or
pnpm exec oxlint --version 2>/dev/null || npx oxlint --version 2>/dev/null
pnpm exec oxfmt --version 2>/dev/null || npx oxfmt --version 2>/dev/null
Read the appropriate mapping and check version staleness:
- Biome:
references/biome-eslint-mapping.json→_meta.biomeVersion - Oxc:
references/oxc-eslint-mapping.json→_meta.oxlintVersion
Compare installed version against _meta version. If installed is newer, warn that mappings may be stale and suggest running update mode.
M2: Gather Active Rules
bash scripts/get-eslint-rules.sh
bash scripts/get-biome-rules.sh # or get-oxlint-rules.sh
Each script needs its tool installed in the project and auto-detects the package manager (pnpm / yarn / bun / npm). If one fails, fall back to reading the configs manually and say in the report which list came from a config read rather than from the tool.
M3: Load References
cat references/biome-eslint-mapping.json # or oxc-eslint-mapping.json
cat references/type-aware-rules.json
For Oxc, also load references/oxc-defaults.json.
M4: Cross-Reference (Core Analysis)
For each active ESLint rule, classify it:
| Category | Meaning | Action |
|---|---|---|
| DISABLE | Target has "same" equivalent AND it's active | Safe to turn off in ESLint |
| REVIEW | Target has "inspired" equivalent AND it's active | Check behavior before disabling |
| ENABLE_TARGET | Target has equivalent but it's NOT active | Can switch by enabling in target config |
| ESLINT_ONLY | No target equivalent exists | Must keep in ESLint |
| TYPE_AWARE | Requires TypeScript type info | Expensive; check if target supports it |
Biome classification:
for each active ESLint rule:
1. Look up in biome-eslint-mapping.json
- Resolution: exact → "eslint-plugin-" prefix → scoped (@scope/rule)
- Core ESLint rules match directly: "no-debugger"
2. No mapping → check type-aware-rules.json → TYPE_AWARE or ESLINT_ONLY
3. Mapping exists:
a. Check if Biome rule is active (match by RULE NAME only, not category)
b. Active + "same" → DISABLE
c. Active + "inspired" → REVIEW
d. Not active → ENABLE_TARGET
Oxc classification:
for each active ESLint rule:
1. Look up in oxc-eslint-mapping.json (same resolution order)
2. No mapping → TYPE_AWARE (if in type-aware list) or ESLINT_ONLY
- Oxlint supports type-aware rules via tsgolint (check "typeAware" field)
3. Mapping exists:
a. Check if active; all Oxlint mappings are "same" relationship
b. Active → DISABLE
c. Not active → ENABLE_TARGET
Classify every active rule before reporting any of them, and end the phase with one line
carrying the counts: 142 active ESLint rules -> 61 DISABLE, 12 REVIEW, 24 ENABLE_TARGET, 38 ESLINT_ONLY, 7 TYPE_AWARE.
M5: Prettier Migration Analysis
If Prettier config found:
Biome: Map Prettier options → Biome equivalents (see table in references/docs.md).
Note: biome migrate prettier --write automates this.
Oxc: Oxfmt uses Prettier-compatible option names. Key differences:
printWidthdefaults to100(Prettier:80)endOfLine: "auto"not supported- Built-in:
sortImports,sortTailwindcss,sortPackageJsonNote:oxfmt --migrate=prettierautomates this.
Import sorting (both targets): Check if the project uses eslint-plugin-import sort rules, @trivago/prettier-plugin-sort-imports, prettier-plugin-organize-imports, or @ianvs/prettier-plugin-sort-imports. If so, recommend enabling the native import sorter:
- Biome:
"organizeImports": { "enabled": true }inbiome.json - Oxfmt:
"sortImports": { ... }(see defaults inoxc-defaults.json)
Tailwind class sorting (both targets): Check if tailwindcss is in project deps. If so, and if prettier-plugin-tailwindcss was in use, recommend enabling native sorting:
- Biome: not built-in — keep the Prettier plugin or use
biome-plugin-tailwindcssif available - Oxfmt:
"sortTailwindcss": { ... }built-in (seeoxc-defaults.json→oxfmt_ifTailwind)
M6: Oxc Config Initialization (Oxc Only)
If no Oxc config exists, offer to create from references/oxc-defaults.json.
Before writing config, check project dependencies:
# Check for React
jq -r '.dependencies.react // .devDependencies.react // empty' package.json
# Check for Tailwind CSS
jq -r '.dependencies.tailwindcss // .devDependencies.tailwindcss // empty' package.json
Config assembly from defaults:
- Always:
categories,rules,env,optionsfromoxlintsection - Always:
plugins.always(eslint,typescript,unicorn,oxc,import) - If React in deps: add
plugins.ifReact(react,react-hooks,jsx-a11y) - Oxfmt:
singleQuote,sortImportsfromoxfmtsection - If Tailwind in deps: merge
oxfmt_ifTailwind→ enablessortTailwindcsswith class sorting forclassName,class, and common helpers (clsx,cn,cva,tw,twMerge,twJoin)
Vite+ projects ($VITEPLUS=true): Write into vite.config.ts lint and fmt blocks. Do NOT create standalone config files. Example:
import { defineConfig } from 'vite-plus';
export default defineConfig({
lint: {
categories: { correctness: 'error', suspicious: 'warn', perf: 'warn' },
plugins: ['eslint', 'typescript', 'unicorn', 'oxc', 'import', 'react', 'react-hooks', 'jsx-a11y'],
rules: {
'no-console': 'warn',
'eqeqeq': 'error',
'import/no-cycle': 'error',
},
options: { typeAware: true, typeCheck: true },
},
fmt: {
singleQuote: true,
sortImports: { ignoreCase: true, order: 'asc', newlinesBetween: true },
sortTailwindcss: { functions: ['clsx', 'cn', 'cva', 'tw'] }, // if Tailwind
},
});
Standalone Oxc projects: Write .oxlintrc.json and .oxfmtrc.json.
M7: Performance Analysis (Audit Mode Only)
TIMING=1 pnpm exec eslint 'src/**/*.{ts,tsx}' 2>&1 | tee "$SCRATCHPAD/eslint-timing.txt"
Cross-reference slowest rules against mapping and type-aware list.
M8: Generate Migration Report
See Report Templates below. Then stop: the report and the ready-to-paste config are the deliverable. Do not edit the ESLint config, write the target config, or run either linter to check the result unless the user asks for the changes to be applied.
Modernize Workflow
Runs when no ESLint/Prettier configs exist but Biome and/or Oxc configs are present.
N1: Read Current Config
Read and parse the tool's config file:
- Biome:
biome.jsonorbiome.jsonc - Oxc standalone:
.oxlintrc.jsonand.oxfmtrc.json - Vite+ (
$VITEPLUS=true): parselintandfmtblocks fromvite.config.ts
N2: Get Installed Version
pnpm exec biome --version 2>/dev/null || npx biome --version 2>/dev/null
# or
pnpm exec oxlint --version 2>/dev/null || npx oxlint --version 2>/dev/null
N3: Load Version History
cat references/biome-versions.json # or oxc-versions.json
Determine which versions the user has passed through since their config was last updated. Use the installed version to find applicable changes.
N4: Check Nursery Promotions (Biome)
Scan the user's config for any rules referencing nursery/ category. Cross-reference against biome-versions.json → promotedFromNursery entries for the installed version and all prior tracked versions.
For each stale nursery reference found:
| Current (stale) | Should be | Promoted in |
|---|---|---|
nursery/noConsole |
suspicious/noConsole |
v2.4 |
nursery/noSecrets |
security/noSecrets |
v2.3 |
Generate a ready-to-apply diff or config patch.
N5: Suggest New Quality Rules
Biome: Check biome-versions.json for newRules in versions newer than user's mapping date. Highlight rules that are:
- Promoted to stable (non-nursery) — safe to enable
- Commonly useful (e.g.,
noUnusedExpressions,noImportCycles,noFloatingPromises) - New formatter options (e.g.,
trailingNewlinein v2.4)
Oxc: Check oxc-versions.json for recommendedConfig rules not yet in user's config. Suggest:
- Categories worth enabling (
suspicious,perf) - High-value rules from
oxc-versions.json→recommendedConfig.rules - New features (type-aware linting, Vue support, config extends)
N6: Suggest New Formatter Options and Sorting
Check whether Tailwind is a project dependency first, with the same jq query as M6.
Biome: Check biome-versions.json for newFormatterOptions. Highlight useful additions:
formatter.trailingNewline(v2.4)formatter.lineEnding: "auto"(v2.3)formatter.expandfor array/object controlorganizeImports.enabled: trueif not already on — replaces import-sort plugins- Note: Biome does not have built-in Tailwind class sorting
Oxc/Vite+: Check if Oxfmt features are underutilized:
sortImports(off by default, high value — replaces import-sort plugins)sortTailwindcss(if Tailwind in deps — replacesprettier-plugin-tailwindcss)jsdocformatting (off by default)- For Vite+:
lint.options.typeAwareandlint.options.typeCheck(recommended on)
End the analysis with one line carrying the counts: 3 stale nursery refs, 9 new stable rules, 4 formatter options available.
N7: Generate Modernize Report
## Modernize Report
### Environment
- Tool: Biome vX.x / Oxlint vX.x / Vite+ vX.x
- Config: biome.json / .oxlintrc.json / vite.config.ts (lint + fmt)
- Reference version: X.x (installed: Y.y)
### Nursery Promotions (Biome)
X rules in your config still reference nursery/ but have been promoted:
| Current | Updated | Version |
|---------|---------|---------|
| `nursery/noSecrets` | `security/noSecrets` | v2.3 |
### New Rules Available
These rules are stable and recommended but not in your config:
| Rule | Category | Description |
|------|----------|-------------|
| `noImportCycles` | suspicious | Detect circular imports |
### New Formatter Options
| Option | Value | Since |
|--------|-------|-------|
| `trailingNewline` | `true` | v2.4 |
### Ready-to-Apply Config Patch
(JSON diff to paste into config)
Update Workflow
Refreshes all reference files from upstream.
U1: Update the ESLint mapping files
| File | Source | _meta fields to bump |
|---|---|---|
references/biome-eslint-mapping.json |
https://biomejs.dev/linter/rules-sources/ |
biomeVersion, updatedAt |
references/oxc-eslint-mapping.json |
https://oxc.rs/docs/guide/usage/linter/rules |
oxlintVersion, updatedAt |
For each: fetch, parse the rule mappings (Oxc's are grouped by plugin), diff against the
existing file, show what was added and removed, then bump the _meta fields.
U2: Update type-aware-rules.json
gh api repos/typescript-eslint/typescript-eslint/contents/packages/eslint-plugin/src/configs/flat/disable-type-checked.ts --jq '.content' | base64 -d
Parse, diff, update _meta.updatedAt.
U3: Update version files
Check Biome and Oxc changelogs for new versions not yet tracked:
- Biome:
https://biomejs.dev/blog/— look for newbiome-vX-Yposts - Oxc:
https://oxc.rs/blog/— look for new release posts
For each new version found, add entry to biome-versions.json or oxc-versions.json with:
- Promoted rules
- New rules
- New formatter options
- New features
End with one line per refreshed file: biome-eslint-mapping.json: +14 / -3 (v2.4.2). Then stop
— update maintains this skill's reference files and touches no project config.
Report Templates
Sync Mode (Migration)
Filled with real values from one run; a table with no rows for this run is kept, showing _(none)_.
## Lint Sync Report
### Environment
- ESLint: v9.18.0 (flat config)
- Target: Oxlint v1.14.0 + Oxfmt v0.4.2 (via Vite+ 1.0.3, `vite.config.ts`)
- Mapping version: 2025-11-04 (stale — installed Oxlint is newer, run `update`)
- Prettier config: found (`.prettierrc`)
### Summary
| Category | Count |
|----------|-------|
| DISABLE (safe to remove) | 61 |
| REVIEW (check before removing) | 12 |
| ENABLE_TARGET (can migrate) | 24 |
| ESLINT_ONLY (must keep) | 38 |
| TYPE_AWARE | 7 |
### DISABLE — Safe to Turn Off in ESLint
| ESLint Rule | Target Equivalent |
|-------------|------------------|
| `no-debugger` | `oxc/no-debugger` |
| `@typescript-eslint/no-unused-vars` | `typescript/no-unused-vars` |
### REVIEW — Check Before Disabling
| ESLint Rule | Target Equivalent | Notes |
|-------------|------------------|-------|
| `import/no-cycle` | `import/no-cycle` | Inspired; Oxlint ignores `maxDepth` |
### ENABLE_TARGET — Available to Migrate
| ESLint Rule | Target Equivalent | Relationship |
|-------------|------------------|--------------|
| `eqeqeq` | `eslint/eqeqeq` | same (not yet active) |
### ESLINT_ONLY — Must Keep
| ESLint Rule | Type-Aware? |
|-------------|-------------|
| `@typescript-eslint/no-floating-promises` | yes |
### Prettier Migration (if applicable)
| Prettier Option | Current Value | Target Option | Target Default | Action |
|----------------|--------------|--------------|----------------|--------|
| `printWidth` | 80 | `printWidth` | 100 | set explicitly to keep 80 |
### Ready-to-Paste Config
Audit Mode (adds to Sync)
### Performance Analysis
#### Top 10 Slowest Rules
| Rule | Time (ms) | Type-Aware? | Target Equivalent? |
|------|-----------|-------------|-------------------|
#### Coverage Statistics
- Total active ESLint rules: X
- Covered by target: X (Y%)
- ESLint-only: X (Y%)
- Type-aware: X (Y%)
#### Migration Checklist
- [ ] Disable X covered rules
- [ ] Review X "inspired" rules (Biome)
- [ ] Enable X additional target rules
- [ ] Migrate Prettier config
- [ ] Run both linters and compare output
Bundled References
references/biome-eslint-mapping.json— ESLint → Biome rule mapping (~260 entries)references/oxc-eslint-mapping.json— ESLint → Oxlint rule mapping (~520 entries)references/biome-versions.json— Biome version changelog: promotions, new rules, new optionsreferences/oxc-versions.json— Oxc version changelog: new rules, features, recommended configreferences/oxc-defaults.json— Opinionated Oxlint/Oxfmt defaults for initializationreferences/type-aware-rules.json— TypeScript-ESLint type-aware rules (~60 entries)references/docs.md— Context7 library IDs and doc URLs for Biome, Oxc, and Vite+; read it before any web fetchscripts/get-eslint-rules.sh— Extract active ESLint rules via--print-configscripts/get-biome-rules.sh— Extract active Biome rules viabiome rage --linterscripts/get-oxlint-rules.sh— Extract active Oxlint rules