Skip to content
Development
Skill

/lint-rule-development

Use this skill when creating or modifying Biome lint rules or assists, including analyzer queries, semantic bindings, rule state, code actions, fix safety, options, registration, and end-user rule rustdoc. Also load diagnostics-development for substantial message/advice design

From plugin
biome
26k11 skills
Install
$ npx -y skills add biomejs/biome --skill lint-rule-development --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/lint-rule-development

Context preview

The summary Claude sees to decide when to auto-load this skill.

Use this skill when creating or modifying Biome lint rules or assists, including analyzer queries, semantic bindings, rule state, code actions, fix safety, options, registration, and end-user rule rustdoc. Also load diagnostics-development for substantial message/advice design

SKILL.md

lint-rule-development.SKILL.md
name: lint-rule-development
description: Use this skill when creating or modifying Biome lint rules or assists, including analyzer queries, semantic bindings, rule state, code actions, fix safety, options, registration, and end-user rule rustdoc. Also load diagnostics-development for substantial message/advice design and testing-codegen for fixture or snapshot mechanics.
compatibility: Designed for coding agents working on the Biome codebase (github.com/biomejs/biome).

Lint Rule Development

Follow the current analyzer architecture in `crates/biome_analyze/CONTRIBUTING.md`. Read only the sections relevant to the rule rather than loading the entire guide by default.

Workflow

1. Find two or three current rules in the same language and group with a similar query or action. 2. Generate the matching lint-rule or assist scaffolding when adding analyzer behavior. 3. Implement the narrowest query and state needed to decide whether to signal. 4. Add a diagnostic and, when safe, an action. 5. Add focused valid and invalid fixtures, then inspect snapshots. 6. Run required analyzer codegen and the narrowest tests.

Scaffolding commands:

just new-js-lintrule useMyRule
just new-css-lintrule useMyRule
just new-json-lintrule useMyRule
just new-graphql-lintrule useMyRule

New lint rules start in `nursery`. They are patch changes targeting `main`, because nursery rules do not follow normal feature versioning. Load `changeset` for the release entry.

For a new assist, use the language's `new-*-assistrule` recipe, for example:

just new-js-assistrule useMyAction
just new-json-assistrule useMyAction

The generator places assists under `src/assist/source/`; they do not use lint groups or the nursery policy. A new assist is a user-facing feature and normally requires a minor changeset targeting `next`. A bug fix to an existing assist follows normal bug-fix policy. Check `justfile` for the languages with assist scaffolding.

Query and State

Choose the least expensive query that answers the rule:

  • `Ast<Node>` for syntax-local checks;
  • `Semantic<Node>` when bindings, references, scopes, or globals are required;
  • a service query only when the fact is owned by that service;
  • type inference only when syntax and the semantic model cannot answer the question.

The analyzer guide's query and service sections are canonical for available APIs.

`run()` should decide whether to emit a signal. Keep action-only work in `action()` so it is not performed for every candidate node.

Prefer ranges, syntax nodes, tokens, and compact enums in `State`. A `String`, `Box<str>`, or collection built from syntax text often indicates avoidable allocation; load `syntax-text-handling` before owning source text.

For rules matching a global identifier, prove the reference resolves to the global rather than a local shadow. Cover every relevant member of node unions and framework-specific syntax families.

Diagnostics

Every diagnostic answers three separate questions:

1. What condition was found? 2. Why is it a problem? 3. What should the user do?

The message answers the first question. Advice answers the second and, when no action exists, the third. A code action and its label normally answer the third when an automated fix exists.

Load `diagnostics-development` for message structure, markup, details, advice, categories, and standalone `Diagnostic` types. Do not duplicate its guidance in the rule implementation.

Actions

Build mutations in `action()`. Set `FixKind::Safe` only when no reachable input changes behavior. If safety depends on assumptions the rule cannot prove, use `Unsafe`.

Test that applying the action:

  • produces valid syntax;
  • preserves comments and trivia;
  • does not trigger the same rule again;
  • preserves semantics for a safe fix;
  • handles every syntax variant accepted by `run()`.

Use existing rules with the same mutation shape as API examples.

Options

Add options only for a real semantic mode or established conflicting preference. Do not add speculative flexibility.

Use the rule's generated or existing file under `crates/biome_rule_options/src/` as the source of truth. Follow the analyzer guide sections **Rule Options**, **Merge**, and **Documenting Options** for current derives, merge behavior, configuration examples, and rustdoc requirements.

Check these integration points:

  • the rule's `type Options`;
  • defaults preserve behavior when the option is absent;
  • extended configurations merge according to the option type's contract;
  • fixture directories with `options.json` cover each behavior;
  • rule rustdoc states every option's default and shows it in use;
  • `just gen-rules` and `just gen-configuration` update registrations and configuration.

If the source ESLint rule has options that `biome migrate eslint` should preserve, load `eslint-migrate-options`.

Rule Documentation

Rustdoc inside `declare_lint_rule!` and `declare_assist_rule!` is end-user website content, not internal API documentation.

  • Start with a single-line behavior summary.
  • Explain why the reported pattern is problematic.
  • Put `### Invalid` examples before `### Valid` examples.
  • Mark examples according to the current rules-check syntax.
  • For every option, state the default, show configuration, and show an applied example.
  • Do not use `ignore` merely to avoid validation.

Inspect neighboring rule documentation and the analyzer guide before writing examples.

Testing and Generation

Load `testing-codegen` for fixture naming, expectation comments, `.jsonc` cases, snapshot review, and pruning.

Typical focused commands:

just test-lintrule useMyRule
just gen-rules
just gen-configuration

Run `just f` and `just l` as required by `AGENTS.md`.

Review Checklist

  • The query is no broader than needed.
  • Globals and shadowing are handled through the semantic model.
  • `State` avoids unnecessary owned source text.
  • Action-only work is deferred to `action()`.

-

Read more
Ships withbiome

A toolchain for web projects, aimed to provide functionalities to maintain them. Biome offers formatter and linter, usable via CLI and LSP.

Get the whole plugin
Stats
25,807
Stars
1,232
Forks
Active
Maintenance
Rust
Language
Apache-2.0
License
14h ago
Last commit
3y ago
Created

Repo: biomejs/biome

Other skills on biome.