bug(rewrite): npm run lint forced into rtk lint on Biome projects
#1489 opened on Apr 24, 2026
Description
Environment
- rtk 0.37.2 (Homebrew,
/opt/homebrew/bin/rtk) - macOS darwin 25.4.0 (arm64)
- Claude Code 2.1.x with the
rtk hook claudePreToolUse hook shipped byrtk init
Repro
Project uses Biome, not ESLint. package.json:
{
"scripts": {
"lint": "biome check"
}
}
rtk rewrite "npm run lint"->rtk lint(exit 0).- Claude Code hook rewrites
npm run linttortk lintbefore execution. rtk lintshells out to biome, biome exits 0 with plain-text output, and the lint adapter attempts to parse biome's output as ESLint JSON.- Observed output:
ESLint output (JSON parse failed: EOF while parsing a value at line 1 column 0), process exit 0.
Expected
npm run lint passes through to the project's actual lint script without being forced into the ESLint adapter. Either:
- route
npm run lintthroughrtk npm run lint(mirrors the fix in PR #678 forpnpm lint), or - detect Biome from
package.json/ presence ofbiome.jsonand dispatch to the existingrtk biomefilter added in #316.
Either way, the adapter's JSON parse should never run on biome output.
Actual
rtk rewrite sends every npm run lint into the ESLint adapter regardless of what the script actually invokes. The adapter fails to parse biome output, prints a JSON-parse-failed warning, and exits 0. Agents see exit 0 and move on; the real lint result is discarded silently.
Reproduced cleanly with an empty-stdout target:
$ rtk lint ./no-op.sh
ESLint output (JSON parse failed: EOF while parsing a value at line 1 column 0)
$ echo $?
0
Also tried
[hooks] exclude_commands = ["npm run lint"]inconfig.toml: no effect onrtk rewriteor the Claude hook (matches #243 / #1335)..rtk/filters.tomlwith a matchingmatch_command: only applies to unhandled commands; cannot override the built-in rewrite (also noted in #1335).RTK_DISABLED=1 rtk lint: still runs the ESLint-JSON parse and still prints the warning. Not an escape hatch for this collision.rtk proxy npm run lint: works per-command but strips all downstream filtering if the agent reflexively applies it more broadly.
Related
- #665 (open)
pnpm lintshows the same symptom on Biome projects. - PR #678 (open, checks green) routes
pnpm lintthroughrtk pnpm lint. Does not covernpm run lint. - PR #443 (open) is the active
NpmCommandssub-enum restructure for smart script routing. As currently written it wiresnpm run lint -> lint_cmd(the ESLint adapter), which codifies this bug rather than fixing it. Addressing the Biome case here, before #443 merges, seems cheapest. - #316 (closed, 0.30.0) added the Biome output filter, but only on the direct
rtk biome lintpath, not through thertk lintrewrite. - #243 / #1335 (both open) document that
[hooks] exclude_commandsis not threaded into the Claude Code hook rewrite path, so config-level opt-out is not currently an option.
Likely cause
src/discover/rules.rs maps any npm run lint (and pnpm lint, biome check, npm run check) to the rtk lint adapter, which assumes ESLint JSON. PR #678 patches the pnpm lint row; the npm run lint row should follow the same routing change. The natural landing spot is PR #443's NpmCommands::Run { script: "lint", .. } branch: either detect Biome from package.json / biome.json and dispatch to the existing biome filter, or fall through to the generic npm_cmd::run_script boilerplate-stripper when detection is uncertain. Either beats running the ESLint JSON parser on biome output.
Impact
AI coding agents see exit 0 with a suspicious-looking warning and often ignore it. Real lint failures (and anything downstream that gates on lint) can pass silently on Biome projects for multiple commits before the regression is noticed by a human.