CLI

Run framework diagnostics from one command.

Run vite-doctor from the project root to get Vite, Vue, Nuxt, and Nitro diagnostics.

Install dependencies first. The CLI does not run package-manager install commands.

Doctor resolves the runtime that the framework actually loads. In a Nuxt project, it follows the installed Nuxt package to its Nitro package and then follows Nitro to H3, so Nitro 3 or H3 v2 advice does not appear for an installed Nitro 2 or H3 v1 runtime. If Doctor cannot identify a runtime safely, it reports the unresolved inventory and suppresses version-specific diagnostics.

Run diagnostics

pnpm dlx vite-doctor .

Narrow the Doctor Run when you need changed files, strict CI failure behavior, one Rule, or safe fixes:

pnpm dlx vite-doctor . --changed
pnpm dlx vite-doctor . --max-warnings 0
pnpm dlx vite-doctor . --rules nuxt/fetch/no-raw-fetch-in-setup
pnpm dlx vite-doctor . --fix

--changed includes staged, unstaged, and untracked files against HEAD. --since <ref> uses the merge base with that ref. Doctor reports only Diagnostics whose source location overlaps changed lines, and the report equals a full run filtered to those lines. Diagnostics without a source range are included when the file they are anchored to changed; manifest Rules anchor project-level findings to the first source file, as in a full run. Parser evidence gaps are reported for changed files only, and the report has no workspace graph summary unless --analyses dead-code or graph is requested.

File Rules that report only into the file they analyze (the default report scope) run on the changed files only. Rules that declare a project report scope, manifest and workspace Rules, and --analyses keep whole-project context, and unchanged files are parsed only for them. With the analysis cache, those project-wide results are recomputed only where a recorded Rule input changed. A Git failure stops the run instead of returning a false clean result.

In a workspace declared by pnpm-workspace.yaml or the workspaces field, Doctor reads every workspace package's package.json. A Rule Pack that the root activates covers the whole workspace. A Rule Pack that only some workspace packages activate runs its file Rules on those packages' files. If the root depends on Nitro and packages/app depends on Vue, the Vue Rules check packages/app only. The JSON and agent reports list each workspace package with its framework and activated Rule Packs under workspacePackages. Passing --framework applies only that framework's Rule Packs, to the whole workspace.

Each Nuxt workspace package keeps its own Nuxt Project Inventory: its nuxt.config, .nuxt/doctor.manifest.json, server directories, and runtime versions. Nuxt Rules read the inventory of the package that owns the file, so a workspace with several Nuxt apps runs as one Doctor Run. Doctor options set through the vite-doctor/nuxt module of a nested Nuxt app apply only when Doctor runs from that app. A workspace run lists them under evidenceGaps.

Safe fixes are applied atomically and Doctor runs again. The final report contains the remaining Diagnostics and a count of applied or skipped edits.

If script parsing reports an error and discards source statements, Doctor marks the Run incomplete and reports the affected file and parser message, even when earlier statements were recovered. Check the source syntax and parser support before relying on the report. Diagnostics from recovered statements and successfully parsed files remain available, including on runs that reuse cached File Facts.

Vue SFC parser errors also make the Run incomplete, including duplicate component blocks and malformed template markup. Doctor retains findings from recovered component content and clears the evidence gap after repair. This does not validate preprocessed templates or every recovered script grammar error.

Use --baseline <file> --new-only to suppress fingerprints already recorded in a baseline. Doctor accepts an array of fingerprint strings or objects with a fingerprint field, an object containing a diagnostics array and optional version: 1, or a Doctor JSON report with reportVersion: 3. In JSON reports, version identifies the Doctor package rather than the baseline format. An existing unreadable or malformed baseline stops the run with DOC0025 and CLI exit code 2. A missing baseline is treated as empty so a first run can initialize it through the updateBaseline Doctor Run option.

Analysis cache

The persistent analysis cache is on by default. Pass --no-cache to run without reading or writing it. Agent rerun commands repeat --cache or --no-cache only when the original run passed one.

The cache is one file, store.json, in .vite-doctor/cache (.nuxt/doctor/cache for Nuxt projects). It keeps File Facts and the Diagnostics of each Rule per file, together with the Rule inputs each Rule read: other files, directory listings, and path checks made through ctx.fs. A cached result is reused only when the file content, the Rule implementation, the Project Inventory, the Rule config, and every recorded input are unchanged. Doctor compares file size, modification time, change time, and inode before reading a file, so an unchanged warm run reads no source file. Rules that declare cacheScope: "none" or a non-deterministic determinism always run.

Inspect the cache with vite-doctor cache status (--format json for automation). It prints the store path, whether the current Doctor build wrote it, entry counts, and what the run that last wrote it read, parsed, reused, and computed. vite-doctor cache clean removes it.

Long-lived Doctor process

Each CLI invocation loads Doctor and reads the cache again. When an agent or script runs Doctor many times in one project, opt in to a long-lived Doctor process that keeps loaded Rule Packs and the parsed cache in memory:

export VITE_DOCTOR_SERVER=1      # every Doctor Run in this shell
pnpm vite-doctor . --server      # one Doctor Run
pnpm vite-doctor . --no-server   # run in this process even when enabled

The first Doctor Run starts one Doctor process per project root. Later runs reuse it, one at a time, and get the same stdout, stderr, and exit code as a direct run. The process is replaced when the Doctor version, Node.js, doctor.config.json, or the CI and color environment differ from the client, and it exits after 15 minutes without runs (VITE_DOCTOR_SERVER_IDLE_MS changes the timeout).

These invocations always run directly: interactive terminals, --config, --host-extensions, --watch, and commands other than a Doctor Run. The Doctor process never loads executable configuration or host-registered Doctor Extensions.

pnpm vite-doctor server status   # pid, memory, runs, and whether this CLI reuses it
pnpm vite-doctor server stop

The socket, state file, and log live in a directory private to your user under the system temporary directory; server status prints their paths.

Watch mode

vite-doctor . --watch runs Doctor, then reruns it whenever a project file changes, reusing loaded Rule Packs and the cache between runs. Each rerun prints a full report. Stop it with Ctrl+C; the exit code is that of the last run. --watch cannot be combined with --fix, --unsafe-fix, or --update-baseline.

Cache recovery after upgrading

An interrupted run of an earlier Doctor version can leave a legacy cache lock. Doctor warns when that lock blocks persistence; diagnostics still use the current source, but cache updates and pruning are skipped until cleanup.

To migrate that cache once:

  1. Stop all Doctor processes sharing the cache, including dev servers using a Plugin Surface and processes in other containers.
  2. From the project root, run pnpm vite-doctor cache clean. Pass the same --config and --framework options used by the original run. For a Plugin Surface with an in-memory custom cache directory, use cleanCache(root, config) from vite-doctor with that configuration after stopping the hosts.
  3. Restart Doctor. The next run rebuilds the cache; subsequent unchanged runs reuse it.

This explicit cleanup is required for legacy .store.lock files and lock claims without a recognized owner namespace. Those formats cannot prove whether a writer is still active, so Doctor never automatically deletes them based on age or a PID lookup. Current-format abandoned locks are recovered automatically when Doctor can establish that the owner has exited in the same PID namespace.

Workspace analyses

Use --analyses to request workspace graph, dead-code, duplication, or health Diagnostics. These analyses use the source inventory and available project entrypoints. Select one analysis or combine them with commas:

pnpm vite-doctor . --analyses graph
pnpm vite-doctor . --analyses dead-code
pnpm vite-doctor . --analyses dupes
pnpm vite-doctor . --analyses health
pnpm vite-doctor . --analyses graph,dead-code,dupes,health --format json

Selecting --analyses alone runs the selected workspace analyses. Add --rules <rule-id> to also run a Rule from a Rule Pack. Review runtime loading, public package consumers, and host conventions before removing code based on a reachability Diagnostic. Each reference page describes its evidence and limits.

Exit codes

  • 0: no blocking Diagnostics or errors, and the warning limit was not exceeded. The report may still contain warnings.
  • 1: Doctor found a blocker or error, or warnings exceeded --max-warnings.
  • 2: the command or configuration is invalid.
  • 3: Doctor could not collect required project evidence, including runtime resolution or source parsing. This takes precedence over Diagnostic severity.

When a command or configuration failure has a Diagnostic Code, text output includes [CODE], JSON and agent failure reports include error.code, and SARIF execution notifications include properties.diagnosticCode. Ordinary failures without a code, such as malformed JSON, omit these fields. Automation can match the code without parsing the error message.

When Doctor runs through Nuxt's nuxt doctor host command, Nuxt currently does not propagate the shim's nonzero exit status. The report still contains the correct Diagnostics, but a CI job that relies on the process status can pass by mistake. Use the direct CLI in CI when Doctor must fail the job:

pnpm vite-doctor . --max-warnings 0
pnpm vite-doctor migrate . --to nuxt@5 --format json

Use Doctor from an agent

Doctor uses Vercel's agent catalog to recognize coding-agent runtimes. Non-interactive agent runs receive the compact agent report automatically. CI and interactive terminals keep text output. Pass an explicit format whenever a script needs a fixed contract:

pnpm dlx vite-doctor . --format agent
pnpm dlx vite-doctor . --format json
pnpm dlx vite-doctor . --format sarif
pnpm dlx vite-doctor . --format text

The agent report removes duplicate fields but keeps a direct remediation path. Each Diagnostic includes its message, remediation, confidence, relative location, edit plan when available, and Diagnostic Reference URL. Top-level command templates cover explanation, focused verification, and the full rerun without repeating them for every finding.

For automation, use the commandArgs arrays. The first item is the executable and the remaining items are literal arguments. Replace the <code> or <rule> item as needed, then run the array without a shell from project.cwd. This preserves revision names and preset selectors containing spaces or shell syntax on every platform. The commands strings are quoted templates for POSIX shells; they are not Windows command-line encodings.

CLI verification commands retain Rule selection, severity, analyses, baseline filtering, warning limits, cache policy, profiling, and an explicitly selected --config path. verify replaces the Rule selector with <rule>. Both commands omit --fix, --unsafe-fix, and baseline updates so verification does not modify source or baselines. Doctor never discovers executable configuration when creating these commands.

API callers can provide { runOptions, configFile } to createAgentReport, or as the third argument to createReport, to preserve the same CLI options. In-memory host configuration and Doctor Extensions cannot be reconstructed from a command; use the original Plugin Surface to repeat those runs.

Use the same explicit config when listing or explaining Rules from a Doctor Extension:

pnpm vite-doctor rules --config doctor.config.ts
pnpm vite-doctor explain CUSTOM0001 --config doctor.config.ts

Both commands register configured Rule Packs, including packs contributed by an extension's setup function, without executing Rules. Executable config loading remains explicit. Agent explanation commands retain the selected config path. Custom codes link to the docs URL declared by their Rule Pack's defineDoctorDiagnostics registry, or to the Rule's reference link. Doctor does not invent a Diagnostic Reference page for a third-party code.

Nuxt modules can register Doctor Extensions through the Nuxt 4 Bridge. nuxt doctor loads them automatically. The standalone CLI loads them only when you pass --host-extensions, because that runs code registered by the host:

pnpm vite-doctor . --host-extensions --max-warnings 0
pnpm vite-doctor explain ACME0001 --host-extensions

Agent commands keep --host-extensions when the original run used it. See Extending Doctor to write and ship Rules.

Check a migration before upgrading

Use migrate to evaluate the current source against the next supported runtime graph:

pnpm dlx vite-doctor migrate .
pnpm nuxt doctor migrate

Doctor infers Nuxt 5 from an installed Nuxt 4 project and Nitro 3 from an installed Nitro 2 project. Pass an explicit target when Doctor cannot infer one unambiguous next step:

pnpm dlx vite-doctor migrate . --to nuxt@5
pnpm dlx vite-doctor migrate . --to nitro@3

The report separates source changes that are safe on the installed runtime, dependency, configuration, and source changes that must land together, and checks that require the target runtime to be installed. The command diagnoses only; it does not rewrite source or dependencies.

Use JSON for automation or migration tooling:

pnpm dlx vite-doctor migrate . --to nuxt@5 --format json

Configuration

Doctor loads doctor.config.json automatically. This form is declarative and does not execute project code:

doctor.config.json
{
  "rules": {
    "vite/define/no-secret-define": "error"
  }
}

Inline doctor-disable directives apply only to Diagnostics with source ranges. To suppress a Diagnostic without a source range, add a suppressions entry with its ruleId, optional file, and a reason.

Use doctor.config.ts only when configuration needs code. Loading executable configuration is an explicit trust decision:

doctor.config.ts
import { defineDoctorConfig } from "vite-doctor/config";

export default defineDoctorConfig({
  rules: {
    "vite/define/no-secret-define": "error",
  },
});

Config Extends selectors use pack/preset. A selector can use a Rule Pack's full name, such as vendor/vite/recommended, or its short name when that name is unique. If multiple installed packs share a short name, Doctor reports an ambiguity instead of choosing based on registration order; use the full Rule Pack name to select the intended preset.

Duplicate full Rule Pack names are rejected during registration with DOC0023, before preset selection. Distinct full names may share short aliases; using an ambiguous short selector reports DOC0024. An exact full name takes precedence over another pack's short alias.

pnpm dlx vite-doctor . --config doctor.config.ts

When Doctor runs through a host integration, pass the same policy through the plugin surface:

vite.config.ts
import { doctor } from "vite-doctor";

export default {
  plugins: [
    doctor({
      config: {
        include: ["src/**", "vite.config.ts"],
        rules: {
          "vite/define/no-secret-define": "error",
        },
      },
    }),
  ],
};

In Vite config, the same plugin covers Vite, Vue, and Nitro projects. Doctor picks the matching diagnostics automatically:

vite.config.ts
import { doctor } from "vite-doctor";

export default {
  plugins: [doctor()],
};

Set framework: "nitro" only when Doctor cannot identify the framework automatically:

vite.config.ts
import { doctor } from "vite-doctor";

export default {
  plugins: [doctor({ framework: "nitro" })],
};

Run in CI/CD

Add a doctor script to the project:

pnpm pkg set scripts.doctor="vite-doctor . --max-warnings 0"

This GitHub Actions example installs dependencies, then runs the package script:

name: Checks

on:
  pull_request:
  push:
    branches: [main]

jobs:
  checks:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version-file: .node-version
      - run: corepack enable
      - run: pnpm install --frozen-lockfile
      - run: pnpm doctor

Record existing findings

Record the current Diagnostic fingerprints, then report only findings that are absent from that baseline:

pnpm vite-doctor . --baseline doctor-baseline.json --update-baseline
pnpm vite-doctor . --baseline doctor-baseline.json --new-only

--update-baseline requires an explicit --baseline path and creates missing parent directories. It replaces the recorded set with the findings from the current Doctor Run, so resolved findings disappear from the next baseline. It still reports findings and uses the usual exit codes. When combined with --new-only, the update also retains the currently suppressed fingerprints.

Nuxt projects

Nuxt projects should use the module and Nuxt host command:

pnpm add -D vite-doctor
nuxt.config.ts
export default defineNuxtConfig({
  modules: ["vite-doctor/nuxt"],
  doctor: {
    rules: {
      "nuxt/routing/prefer-nuxt-useroute": "error",
    },
  },
});
pnpm nuxt doctor

nuxt doctor is the convenient local Nuxt command. In CI, use pnpm vite-doctor when the exit code is part of the check; see Exit codes.

Use the framework guides when you want setup instructions for one runtime:

Copyright © 2026