Agent Skills: Biome TypeScript Linting Skill

Configure and use Biome (biomejs) for TypeScript linting and formatting. Use when setting up Biome in a project, configuring lint rules, migrating from ESLint/Prettier, fixing lint errors, setting up CI pipelines with Biome, or configuring git hooks for code quality. Covers biome.json configuration, file inclusion/exclusion patterns, rule overrides, and integration with build tooling.

UncategorizedID: leynos/agent-helper-scripts/biome-typescript

Install this agent skill to your local

pnpm dlx add-skill https://github.com/leynos/agent-helper-scripts/tree/HEAD/skills/biome-typescript

Skill Files

Browse the full folder contents for biome-typescript.

Download Skill

Loading file tree…

skills/biome-typescript/SKILL.md

Skill Metadata

Name
biome-typescript
Description
>-

Biome TypeScript Linting Skill

Routing Guide

| Task | Section | | --------------------------------- | ------------------------------------------------- | | Install or update Biome | Version Discovery | | Initial setup | Quick Start | | Configure rules | Configuration Patterns | | Include/exclude files | File Targeting | | Fix specific lint errors | See references/lint-solutions.md | | Stricter rules beyond recommended | See references/strict-rules.md | | Migrate from ESLint/Prettier | See references/migration.md | | CI and git hooks | See references/ci-hooks.md |

Version Discovery

Never trust cached knowledge of Biome versions. Query the npm registry:

npm view @biomejs/biome version

Or for all recent versions:

npm view @biomejs/biome versions --json | tail -20

Check release notes for breaking changes:

curl -s https://api.github.com/repos/biomejs/biome/releases/latest | jq -r '.tag_name, .html_url'

Quick Start

Install as dev dependency, pinned to the release these examples target:

npm install --save-dev --save-exact @biomejs/biome@1.9.4
npx biome init

Pin 1.9.4 while following this skill. The $schema URL and the files.include / files.ignore settings below are 1.9.4 spellings: Biome 2.0 replaced both keys with files.includes and moved organizeImports under assist.actions.source.organizeImports. $schema must always match the installed version, so run biome migrate --write and update $schema in the same commit as any upgrade of Biome itself; see Version Discovery.

This creates biome.json. Immediately verify:

npx biome check .

Minimal biome.json

{
  "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
  "vcs": {
    "enabled": true,
    "clientKind": "git",
    "useIgnoreFile": true
  },
  "organizeImports": {
    "enabled": true
  },
  "linter": {
    "enabled": true,
    "rules": {
      "recommended": true
    }
  },
  "formatter": {
    "enabled": true,
    "indentStyle": "space",
    "indentWidth": 2
  }
}

Critical: Update the $schema URL to match your installed version.

Configuration Patterns

Rule Severity Levels

{
  "linter": {
    "rules": {
      "recommended": true,
      "suspicious": {
        "noExplicitAny": "error",
        "noArrayIndexKey": "warn"
      },
      "style": {
        "noNonNullAssertion": "off"
      }
    }
  }
}

Levels: "error" | "warn" | "off"

Rules with Options

Some rules accept configuration objects:

{
  "linter": {
    "rules": {
      "style": {
        "useNamingConvention": {
          "level": "error",
          "options": {
            "strictCase": false,
            "conventions": [
              {
                "selector": { "kind": "variable" },
                "formats": ["camelCase", "CONSTANT_CASE"]
              }
            ]
          }
        }
      },
      "complexity": {
        "noExcessiveCognitiveComplexity": {
          "level": "error",
          "options": {
            "maxAllowedComplexity": 15
          }
        }
      }
    }
  }
}

Extending Configurations

{
  "extends": ["./biome.base.json"]
}

Arrays merge; later entries override earlier ones.

File Targeting (Rough Edges)

Global Include/Exclude

Top-level files applies to all tools (linter, formatter, organizeImports):

{
  "files": {
    "include": ["src/**", "tests/**"],
    "ignore": ["**/generated/**", "**/*.d.ts", "**/dist/**"]
  }
}

Gotcha: Patterns are relative to biome.json location. Use **/ prefix for recursive matching.

Tool-Specific Include/Exclude

Each tool can have its own file scope:

{
  "linter": {
    "include": ["src/**"],
    "ignore": ["src/generated/**"]
  },
  "formatter": {
    "include": ["src/**", "scripts/**"],
    "ignore": ["src/vendor/**"]
  }
}

Per-File Rule Overrides

Apply different rules to specific file patterns:

{
  "overrides": [
    {
      "include": ["**/*.test.ts", "**/*.spec.ts"],
      "linter": {
        "rules": {
          "suspicious": {
            "noExplicitAny": "off"
          }
        }
      }
    },
    {
      "include": ["scripts/**"],
      "linter": {
        "rules": {
          "suspicious": {
            "noConsole": "off"
          }
        }
      }
    },
    {
      "include": ["**/*.config.ts", "**/*.config.js"],
      "linter": {
        "rules": {
          "style": {
            "noDefaultExport": "off"
          }
        }
      }
    }
  ]
}

Critical ordering: Overrides apply in array order. Later overrides win for the same file.

VCS Integration

Let Biome respect .gitignore:

{
  "vcs": {
    "enabled": true,
    "clientKind": "git",
    "useIgnoreFile": true,
    "defaultBranch": "main"
  }
}

With useIgnoreFile: true, anything in .gitignore is automatically excluded.

Command Reference

# Check without modifying (CI mode)
npx biome check .

# Fix all auto-fixable issues
npx biome check --write .

# Lint only (no formatting)
npx biome lint .

# Format only
npx biome format --write .

# Organize imports only
npx biome check --organize-imports-enabled=true --write .

# Check specific files
npx biome check src/index.ts src/utils/**/*.ts

# Output as JSON (for tooling)
npx biome check --reporter=json .

# Show which files would be processed
npx biome check --files-ignore-unknown=true --no-errors-on-unmatched .

Useful Flags

| Flag | Purpose | | -------------------------- | ---------------------------------------------- | | --write | Apply fixes | | --unsafe | Apply unsafe fixes (review carefully) | | --staged | Only check git-staged files | | --changed | Only check files changed since default branch | | --reporter=json | Machine-readable output | | --diagnostic-level=error | Exit non-zero only on errors (ignore warnings) |

Suppression Comments

Suppress specific rules inline:

// biome-ignore lint/suspicious/noExplicitAny: external API requires any
const response: any = await legacyApi.fetch();

// biome-ignore lint/style/noNonNullAssertion: checked above
const element = document.getElementById("root")!;

Always include a reason after the colon. Biome enforces this—reasonless suppressions fail.

Avoid these lazy patterns:

// BAD: Will need revisiting
// biome-ignore lint/suspicious/noExplicitAny: TODO fix later
// biome-ignore lint/suspicious/noExplicitAny: too complex to type

// GOOD: Explains why suppression is necessary
// biome-ignore lint/suspicious/noExplicitAny: FFI boundary with untyped C library
// biome-ignore lint/suspicious/noExplicitAny: generic deserializer, caller provides type

Package.json Scripts

{
  "scripts": {
    "lint": "biome check .",
    "lint:fix": "biome check --write .",
    "format": "biome format --write .",
    "check": "biome check --write --unsafe ."
  }
}

TypeScript Integration

Biome only reads tsconfig.json for import path resolution when the nearest tsconfig.json can supply compilerOptions.baseUrl or compilerOptions.paths. Support for both starts in Biome 2.3. The examples on this page pin 1.9.4, where an alias such as @app/foo is not resolved — use relative imports, or upgrade to 2.3 or newer before relying on an alias:

// tsconfig.json
{
  "compilerOptions": {
    "baseUrl": "./src",
    "paths": {
      "@app/*": ["./app/*"]
    }
  }
}

With Biome 2.3 or newer, the nearest tsconfig.json is picked up without any Biome-side setting: baseUrl alone resolves bare specifiers from that directory (import { foo } from "foo" finds src/foo.ts), and paths adds explicit prefix mappings on top of it.

JSX and globals configuration live under javascript and are independent of path resolution. The classic JSX runtime needs React declared as a global because it emits React.createElement calls:

{
  "javascript": {
    "jsxRuntime": "reactClassic",
    "globals": ["React"]
  }
}

For JSX with the automatic runtime, no global is required:

{
  "javascript": {
    "jsxRuntime": "automatic"
  }
}

When to Consult Reference Files

  • Struggling with a specific lint error? → references/lint-solutions.md
  • Want stricter rules than "recommended"? → references/strict-rules.md
  • Migrating from ESLint or Prettier? → references/migration.md
  • Setting up CI or git hooks? → references/ci-hooks.md