The latest releases, changes, and improvements to Ultracite across every version.
24b0d27: Wire up the nestjs ESLint preset to actually enforce rules. Previously the preset exported an empty const config = [], meaning users who imported ultracite/eslint/nestjs got nothing. It now layers @darraghor/eslint-plugin-nestjs-typed (22 rules covering NestJS conventions, dependency injection correctness, and class-validator/Swagger usage) using the same dynamic-enable pattern as the other framework presets.
Consumers who already had the empty preset in their config may see new violations on first run.
161418a: Add missing Biome stable rules to the core config:
suspicious/noDuplicateDependencies → "error" — flags a dependency listed multiple times in the same group, or across dependencies and devDependencies, in package.json.suspicious/useDeprecatedDate → "off" — GraphQL-only convention requiring a deletionDate argument on @deprecated; too opinionated for the default preset.9a2b548: Pin @angular-eslint/eslint-plugin to ^21.3.1 in packages/cli/package.json. Previously declared as "latest", which defeats lockfile reproducibility and means each bun install could pull a newer version than what was tested at publish time. The current resolved version (21.3.1) is unchanged.
44f6d7f: Align ESLint presets with the oxlint configs (the maintained source of truth). Mostly tightens ESLint where oxlint was stricter; a few documented behavioural exceptions oxlint carries (rule conflicts, bun:test compat) are mirrored back.
core — eslint.mjs now enforces complexity, no-unused-private-class-members, sort-keys, sort-vars, and full prefer-destructuring (object + array). typescript.mjs now enforces no-confusing-void-expression, no-misused-promises, prefer-readonly, strict-boolean-expressions, and sets return-await: ["error", "always"]. import.mjs now sets consistent-type-specifier-style: ["error", "prefer-top-level"].
next — added next-env.d.ts override that disables import-x/no-unassigned-import on the generated file.
remix — added routeTree.gen.ts override that disables unicorn/filename-case and unicorn/no-abusive-eslint-disable on the generated file.
react — disabled react/jsx-boolean-value, react/no-unknown-property, and react/only-export-components to match oxlint.
jest — broadened test globs to **/*.{test,spec}.{ts,tsx,js,jsx} + **/__tests__/**/*.{ts,tsx,js,jsx} (previously missed *.spec.* and __tests__/). Disabled no-empty-function and promise/prefer-await-to-then in test scope. Disabled jest/require-hook, jest/no-conditional-in-test, jest/no-hooks, jest/prefer-expect-assertions to mirror oxlint's bun:test/mocking accommodations.
vitest — same test-glob broadening; same no-empty-function / promise/prefer-await-to-then test-scope disables. Removed the prefer-importing-vitest-globals and prefer-to-have-been-called-times disables (oxlint enforces these). Added prefer-lowercase-title: off and valid-title: off to resolve the documented conflict with prefer-describe-function-title (#665).
63f6a18: Drop the redundant react-hooks/exhaustive-deps: "error" override in config/eslint/react/rules/react-hooks.mjs. The dynamic-enable pattern already sets every non-deprecated react-hooks/* rule to "error", so the override was dead code. No behavior change.
5a0ce67: Refresh the misleading header comment in config/eslint/core/rules/eslint-typescript.mjs. The disables for the formatting rules (brace-style, comma-dangle, indent, etc.) used to defer to @typescript-eslint's typed equivalents, but those rules were removed in v8. They're now disabled because Prettier/Oxfmt owns formatting. Updated the comment to reflect the actual rationale.
d681f70: Clean up config/eslint/core/rules/typescript.mjs: remove 22 stale overrides that referenced rules no longer present in @typescript-eslint/eslint-plugin v8.
Most were formatting rules moved out to @stylistic (block-spacing, brace-style, comma-dangle, comma-spacing, func-call-spacing, indent, key-spacing, keyword-spacing, lines-around-comment, lines-between-class-members, member-delimiter-style, no-extra-parens, object-curly-spacing, padding-line-between-statements, quotes, semi, space-before-blocks, space-before-function-paren, space-infix-ops, type-annotation-spacing). The remaining two (no-type-alias, sort-type-union-intersection-members) were removed/deprecated upstream. All were dead no-ops — no behavior change.
3e08c25: Fix ultracite init failing with npm error No workspaces found! in npm monorepos. When isMonorepo() was true, nypm was passed workspace: true, which translates to --workspaces for npm — that installs in every workspace package and errors when patterns match nothing. We now skip the workspace flag for npm (the default root install is what we want) while preserving the flag for pnpm (--workspace-root) and yarn classic (-W). Applies to ultracite, husky, lefthook, and lint-staged installs.aba89bb: Add new oxlint 1.63.0 rules:
eslint/logical-assignment-operators → "error" — prefer ||=, &&=, ??= over their longhand equivalents; aligns with the modern-JS baseline.eslint/require-unicode-regexp → "error" — require the u (or v) flag on regex literals for correct Unicode handling.eslint/no-restricted-properties → "off" — purely a project-specific allowlist; no useful default to enforce.unicorn/no-negated-condition → "error" — newly split from the eslint version; the unicorn variant additionally covers ternary expressions and complements the existing eslint/no-negated-condition.jsx-a11y/interactive-supports-focus → "error" — interactive elements (click handlers, role="button", etc.) must be keyboard-focusable; matches the rest of the a11y baseline.vue/return-in-computed-property → "error" — computed properties must return a value; missing return silently breaks reactivity.vue/no-deprecated-model-definition → "error" — flags Vue 2 model: { ... } usage; Vue 3 is the supported target.vitest/prefer-mock-return-shorthand → "error", vitest/no-unneeded-async-expect-function → "error", vitest/prefer-to-have-been-called-times → "error", vitest/prefer-snapshot-hint → "error" — newly split out from the jest plugin; mirrors the existing jest config which has all four enabled.vitest/require-hook → "off" — newly split out from jest; disabled to mirror jest config (bun:test mock.module() must be called at top level).522155e: Set typescript/return-await to ["error", "always"] to resolve a circular conflict between eslint/require-await, typescript/promise-function-async, and typescript/return-await on Promise-returning functions outside try/catch. With the default in-try-catch mode, autofixers chase each other: promise-function-async adds async, require-await then demands an await, and return-await removes any return await outside a try/catch — leaving no resolvable state. The "always" mode keeps return await everywhere, breaking the cycle while preserving consistent stack traces.
f584d93: Disable unicorn/number-literal-case due to oxc-project/oxc#21949.
ef5c3ae: Fix ultracite check and ultracite fix short-circuiting after the formatter step. Previously, when the formatter (oxfmt or Prettier) exited non-zero, the linter (oxlint, ESLint, Stylelint) was never invoked, hiding lint errors until formatting was clean. The commands now run every step, accumulate failures, and exit with the first failing tool's status. Fixes #690.
3ecb159: Fix the generated oxfmt.config.ts template, which used extends: [ultracite] — a key oxfmt does not recognize, so the preset was silently dropped and built-in options like sortImports never took effect. The template now spreads the preset (...ultracite) so its options are actually applied. Fixes #689.
5a18ec8: Add new oxlint 1.61.0 and 1.62.0 rules:
eslint/func-name-matching → "error" — function names should match the variable they're assigned to; matches the project's strict baseline.eslint/no-underscore-dangle → "off" — common patterns like _id (Mongo) and _internal make this rule too noisy in practice.typescript/explicit-member-accessibility → "off" — forcing public/private on every class member is verbose and not idiomatic in modern TS.jest/prefer-expect-assertions → "off" and vitest/prefer-expect-assertions → "off" — requiring expect.assertions(n) in every test is too strict for general use; not all tests need explicit assertion counts.vitest/max-expects → "error" and vitest/max-nested-describe → "error" — newly split out from the jest plugin; mirrors the existing jest config which has both enabled.vitest/no-conditional-in-test → "off" — newly split out from jest; disabled to mirror jest config (mock factories use conditionals for path-based routing).vitest/no-hooks → "off" — newly split out from jest; disabled to mirror jest config (bun:test uses beforeEach for mock.restore()).react/forbid-component-props → "off" — parity with the ESLint config, which already disables this rule.5be860c: Automatically detect frameworks during the init process.10d9e95: Support -v as a short alias for --version on the CLI (previously only -V worked).8ff1b96: Fix update command not migrating legacy ultracite/<name> extends entries to ultracite/biome/<name> (e.g. ultracite/core, ultracite/react, ultracite/type-aware, etc.).5e055ce: Ignore Cloudflare Workers' generated worker-configuration.d.ts (produced by wrangler types), matching the existing handling of next-env.d.ts.9cc7416: Add a universal editor target that creates .vscode/settings.json for every VS Code-based editor (VS Code, Cursor, Windsurf, CodeBuddy, Antigravity, IBM Bob, Kiro, Trae, Void) with a single selection. The init prompt now offers a "Universal" option, and --editors universal works as an alias on the CLI.2fbded9: Disable the typescript/prefer-readonly-parameter-types Oxlint rule. While the rule is useful for user-authored types, it fires on virtually every parameter that touches a third-party type (Express Request/Response, React events, Node Buffer, ORM models, DOM APIs) because those types aren't deeply readonly internally — leaving users with unfixable violations. Matches the existing ESLint config, which already has this rule off.617affd: Fix dist/, .next/, **/*.gen.*, and other strong-negation (!!) ignore globs being dropped when a consumer's biome.jsonc extends ultracite/biome/core and also defines its own files.includes. The globs moved into config/shared/ignores.jsonc in 7.5.9 were transitively extended through biome/core, and Biome's extend merge doesn't carry files.includes through a two-level chain when the middle config lacks its own entry. The patterns are now inlined directly in biome/core's files.includes (still generated from config/shared/ignores.mjs), matching the pre-7.5.9 behavior.d681e08: Remove the nonexistent import-x/enforce-node-protocol-usage rule from the ESLint core config, which caused ESLint 9 to throw Could not find "enforce-node-protocol-usage" in plugin "import-x". Node protocol enforcement is already covered by unicorn/prefer-node-protocol.67227c9: Enable additional Biome rules across core and framework presets. Core gets noUselessCatchBinding, noUselessUndefined, useConsistentArrowReturn, noUnusedExpressions, and CSS noEmptySource (with useMaxParams and noJsxLiterals turned off); Next.js adds noNextAsyncClientComponent; Qwik adds useQwikMethodUsage and useQwikValidLexicalScope; React adds noReactForwardRef; Vue adds noVueDataObjectDeclaration, noVueDuplicateKeys, noVueReservedKeys, noVueReservedProps, and noVueSetupPropsReactivityLoss.f506624: Enable newly-available rules from oxlint 1.160.0. Core: typescript/no-unnecessary-qualifier, typescript/no-unnecessary-type-parameters, typescript/no-useless-default-assignment, typescript/prefer-readonly-parameter-types, typescript/strict-void-return, unicorn/consistent-template-literal-escape. Jest: jest/prefer-ending-with-an-expect, jest/prefer-importing-jest-globals, jest/valid-expect-in-promise. Vitest: vitest/valid-expect-in-promise.77e9b41: Consolidate build-output, framework-generated, and lockfile ignore patterns (**/dist, **/.next, **/.turbo, **/*.gen.*, bun.lock, pnpm-lock.yaml, etc.) into a single canonical list at config/shared/ignores.mjs. A generated config/shared/ignores.jsonc is provided for Biome's extends, and the same module is imported directly by oxlint, oxfmt, and ESLint so every tool ignores the same paths.73fc21c: Throw a descriptive error when package.json contains invalid JSON instead of surfacing a raw SyntaxError from updatePackageJson. Detect and report when a linter subprocess is terminated by a signal in run-command. Route tsconfig update warnings through @clack/prompts' log.warn for consistent CLI output, and silently return when no tsconfig.json files are found.63f7426: Migrate remaining json parsing to jsonc-parseraa199d1: fix conflicting prefer-describe-function-title / valid-title rules in vitest402908e: Replace custom yaml parser with dependency3dbfe5c: Validate framework name to prevent injectiona2cdc0f: Warn if the file looks like it has ultracite config but we couldn't parse it95718bb: Use cross-spawn for cross-platform spawn compatibilityd09174b: Ignore .open-next in the Biome and ESLint core presets.71aeca4: Remove remaining execSync callse81a604: Add zod for safer json parsingc35a1b3: Speed up the doctor command by running diagnostic checks synchronously with readFileSync/existsSync in a single pass instead of awaiting each check behind its own spinner, removing the per-check async overhead and startup delay.56e4c00: Remove process.exit() - swap with typed Errord35d03c: Switch hot-path init and agent/editor/hook setup calls from async mkdir/readFile to their sync counterparts where I/O already happens serially, and batch the second package.json write (to add check/fix scripts) into the same updatePackageJson call that writes devDependencies in no-install mode, saving a redundant read/write per init.ee224a6: Read positional file arguments from Commander's action callback directly in the check and fix commands instead of re-parsing process.argv to separate files from passthrough flags.a2b7a46: Rewrite doctor around detectLinter so it only runs the checks relevant to the linter configured in the project (Biome, ESLint, or Oxlint) instead of running every check unconditionally. Generalize installation checks into a single checkToolInstallation helper and add detection for Prettier and Stylelint configuration conflicts.cf4a044: Fix angular eslint plugin typo25eb24f: Pass the full dependency list to addDevDependency in one call during init instead of looping per-package, so package managers resolve and install all Ultracite devDependencies in a single invocation.b46537a: Convert exists() and detectLinter() in utils.ts from async (fs.promises.access) to sync (accessSync), and update all call sites across the linter, agent, editor, hook, and integration modules. The function is used in hot init paths where the async overhead gave no benefit.a63d9c5: Replace oxlint's categories block (correctness, pedantic, perf, restriction, style, and suspicious all set to error) in the core preset with an explicit, exhaustively-listed rule set. Category-level defaults were letting rules not present in the core config leak through to consumer configs that extended ours, producing unexpected violations.d18d0e7: Configure Prettier with frameworks context1d6de0d: Add declaration files for ultracite/oxlint/* and ultracite/oxfmt so TypeScript config imports resolve without ts(7016) errors.1073f34: Ensure init'ed JSON files have newlinesacf4a97: Add newly-available oxlint jest rules: jest/no-unneeded-async-expect-function, jest/padding-around-after-all-blocks, jest/prefer-mock-return-shorthand, and jest/prefer-snapshot-hint. Reorganize the config so disabled rules (with their rationale comments) sit at the bottom of the rules block for readability.6905932: Fix vitest/no-importing-vitest-globals conflict4e4dc03: Add newly-available oxlint vitest rules: vitest/consistent-each-for, vitest/hoisted-apis-on-top, vitest/prefer-called-exactly-once-with, vitest/prefer-called-once, vitest/prefer-describe-function-title, vitest/prefer-expect-type-of, vitest/prefer-import-in-mock, vitest/require-awaited-expect-poll, and vitest/require-mock-type-parameters. Drop vitest/prefer-lowercase-title.6a583d1: Fix the generated oxfmt.config.ts template, which was re-exporting Ultracite's config directly (a form oxfmt doesn't accept). Wrap it in defineConfig({ extends: [ultracite] }) so oxfmt picks up the shared rules.5437f81: Rename bundled oxlint and oxfmt config files from .ts to .mjs so consumers can load them without a TypeScript loader, and update Ultracite's own generated oxlint.config.ts/oxfmt.config.ts imports to match.66999e0: Rename bundled oxlint and oxfmt configs from .js back to .ts, and fix the Next.js and React oxlint preset imports that were still pointing at the old extension.97c3938: Fix oxlint and oxfmt import paths22df7a5: Rename bundled oxlint and oxfmt configs from .ts to .js so consumers importing ultracite/oxlint/* no longer need a TypeScript loader at runtime, and update Ultracite's own generated oxlint.config.ts imports to match.e96c55a: Switch oxlint.config.ts to js imports7861cf7: Migrate oxlint and oxfmt configurations from JSON to TypeScript using defineConfig. The CLI now generates oxlint.config.ts and oxfmt.config.ts instead of .oxlintrc.json and .oxfmtrc.jsonc, and all internal framework presets have been converted to TypeScript.fdb1493: Exclude package manager lock files (bun.lock, bun.lockb, package-lock.json, yarn.lock, pnpm-lock.yaml) from Biome linting and formatting94e770e: Remove non-existent oxlint rules (import/no-unresolved, vitest/no-done-callback) for compatibility with oxlint 1.58.0+7a14fb2: Prompt users to install the reusable Ultracite skill during ultracite init and add a --install-skill flag for non-interactive setup.4f0cd02: Fix incorrect react-perf rule names (react_perf → react-perf)f78c934: Group agents that share the same rules file path (e.g. AGENTS.md) under a single prompt option during ultracite init instead of listing every agent individually, so selecting one creates the file once for all compatible agents. Adds a "Universal" option that writes AGENTS.md for the full set of agents that support it.969b271: Add Svelte and Tailwind CSS plugins to Prettier configc189cf1: Add support for new agent integrations including Zencoder, Ona, OpenClaw, Continue, Snowflake Cortex, Deepagents, Qoder, Kimi CLI, Kode, MCPJam, Mux, Pi, Neovate, Pochi, and AdaL, plus add CodeBuddy as a supported editor.04d8455: Add no-void rule with allowAsStatement to complement no-floating-promisese38d579: Fix DEP0190 deprecation warnings in check, fix, and doctor by routing CLI subprocesses through a shared cross-spawn runner with shell: false, while preserving Windows command resolution and direct file-path argument passing.98cb8c2: Pin ESLint initialization to a peer-compatible dependency set so ultracite init no longer installs an incompatible eslint@latest with eslint-plugin-githubfd7d05f: Disable conflicting vitest/prefer-called-times oxlint rule to resolve conflict with vitest/prefer-called-once581ea40: Add typed ultracite/oxlint exports for use in oxlint.config.ts.8ffeb33: Add support for .oxlintrc.mjs and oxlint.config.tsf84edff: Fix --type-aware for Biomeacf301c: Migrate from eslint-plugin-import to import-x5749eb1: Split Jest and Vitest rules out of the core presets into their own opt-in framework presets (ultracite/biome/jest, ultracite/biome/vitest, ultracite/eslint/jest, ultracite/eslint/vitest, ultracite/oxlint/jest, ultracite/oxlint/vitest), selectable through the framework selector during init. Resolves the oxlint rule overlap (#604) where jest and vitest blocks were both applied to every file in the core config.0d27e68: fix noUnusedImports removing new imports in agent hooks3cd6e7b: Upgrade Biome to 2.48db75d7: Only run shell: true on windows0d21c46: Restore shell for windowsfe9acf6: Use local binariesc8fdacf: fix: detectLinter() doesn't walk up directory tree, Breaks monorepo subdirectory usagec79c3b0: Fix lefthook file configuration4b9d206: Make useBlockStatements fix safe8e9e728: Add support for NestJSd0ae8f3: Fix: Biome removes all imports in Svelte files on save instead of organizing them34c79bb: Fix conflicting oxlint rulesc60533d: Fix oxlint import/consistent-type-specifier-stylec201da4: Switch to commander.js, fix multiple agents argse022697: Add support for all provider flags702f6b5: Upgrade Biome to 2.3.11ab47642: add --type-aware and --type-check flags for oxlint7e9b76c: Fix legacy imports92eaa89: Cleanup dist files246c1fc: Update docs and README
Remove catalog dependenciesa8408b6: Fix bundling issuesc4a205f: Remove i18n docsc4a205f: Remove custom reporterc4a205f: Scaffold support for ESLint and Oxlintc4a205f: Add support for Amazon Q, Crush, Firebender, OpenCode, Qwen and Traec4a205f: Move biome config under biome