{"_id":"@dhiravpatel/node-doctor","_rev":"2-887614ef241b1628a9ff5fc7971185fc","name":"@dhiravpatel/node-doctor","dist-tags":{"latest":"1.0.1"},"versions":{"1.0.0":{"name":"@dhiravpatel/node-doctor","version":"1.0.0","keywords":["node","static-analysis","linter","security","express","fastify","prisma","async","sast","code-review","ai","agent"],"author":{"name":"Dhirav Patel"},"license":"MIT","_id":"@dhiravpatel/node-doctor@1.0.0","maintainers":[{"name":"dhiravpatel","email":"dhiravpatel07@gmail.com"}],"homepage":"https://node-doctor.vercel.app/","bugs":{"url":"https://github.com/DhiravPatel/node_doctor/issues"},"bin":{"node-doctor":"bin/node-doctor.js"},"dist":{"shasum":"5aecfdcb16caad3dda3cb06d3e8a5d03d14c01c3","tarball":"https://registry.npmjs.org/@dhiravpatel/node-doctor/-/node-doctor-1.0.0.tgz","fileCount":463,"integrity":"sha512-DDB4qFSlloxKXg6ErgTbb6o0Xbq+2v3fiD3H0CmV9YNHzZkQyWYwa2TNyY3fEySm30lX8vFuT9l0EUX+dpoScg==","signatures":[{"sig":"MEUCIFitYg03P8xYGVCl5//OfWdESOx3wFbByIvX81Ya/QtyAiEAikPeRUUFR1s0Sex9IYl5Yl2JYfi7GQIJKQdiBMZWUKs=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@dhiravpatel%2fnode-doctor@1.0.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":1822638},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=20.19.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./eslint":{"types":"./dist/adapters/eslint.d.ts","import":"./dist/adapters/eslint.js"},"./oxlint":{"types":"./dist/adapters/oxlint.d.ts","import":"./dist/adapters/oxlint.js"},"./package.json":"./package.json"},"gitHead":"92672b6b56fe1e5e83a8bce7a9b49133679a2962","scripts":{"fuzz":"node bench/fuzz.ts","scan":"node src/cli/run.ts","test":"node --test","bench":"node bench/corpus.ts","build":"tsc -p tsconfig.build.json","clean":"node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\"","gen:web":"node scripts/gen-web-rules.ts","prepack":"npm run build","check:web":"node scripts/gen-web-rules.ts --check","typecheck":"tsc -p tsconfig.json","gen:schema":"node scripts/gen-config-schema.ts","check:schema":"node scripts/gen-config-schema.ts --check","gen:registry":"node scripts/gen-registry.ts","test:coverage":"node --test --experimental-test-coverage","check:registry":"node scripts/gen-registry.ts --check"},"_npmUser":{"name":"dhiravpatel","email":"dhiravpatel07@gmail.com"},"repository":{"url":"git+https://github.com/DhiravPatel/node_doctor.git","type":"git"},"_npmVersion":"10.9.8","description":"Deterministic, offline-first static analysis for Node.js backends — catches the defects that compile, pass tests, and fail under concurrency.","directories":{},"_nodeVersion":"22.23.1","dependencies":{"fast-glob":"^3.3.2","oxc-parser":"^0.140.0","picocolors":"^1.1.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^7.0.2","@types/node":"^26.1.1"},"_npmOperationalInternal":{"tmp":"tmp/node-doctor_1.0.0_1785498852576_0.194297737180672","host":"s3://npm-registry-packages-npm-production"}},"1.0.1":{"name":"@dhiravpatel/node-doctor","version":"1.0.1","description":"Deterministic, offline-first static analysis for Node.js backends — catches the defects that compile, pass tests, and fail under concurrency.","keywords":["node","static-analysis","linter","security","express","fastify","prisma","async","sast","code-review","ai","agent"],"license":"MIT","author":{"name":"Dhirav Patel"},"homepage":"https://node-doctor.vercel.app/","repository":{"type":"git","url":"git+https://github.com/DhiravPatel/node_doctor.git"},"bugs":{"url":"https://github.com/DhiravPatel/node_doctor/issues"},"type":"module","engines":{"node":">=20.19.0"},"bin":{"node-doctor":"bin/node-doctor.js"},"main":"dist/index.js","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./eslint":{"types":"./dist/adapters/eslint.d.ts","import":"./dist/adapters/eslint.js"},"./oxlint":{"types":"./dist/adapters/oxlint.d.ts","import":"./dist/adapters/oxlint.js"},"./package.json":"./package.json"},"scripts":{"build":"tsc -p tsconfig.build.json","clean":"node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\"","typecheck":"tsc -p tsconfig.json","test":"node --test","test:coverage":"node --test --experimental-test-coverage","gen:registry":"node scripts/gen-registry.ts","check:registry":"node scripts/gen-registry.ts --check","gen:schema":"node scripts/gen-config-schema.ts","check:schema":"node scripts/gen-config-schema.ts --check","gen:web":"node scripts/gen-web-rules.ts","check:web":"node scripts/gen-web-rules.ts --check","scan":"node src/cli/run.ts","bench":"node bench/corpus.ts","prepack":"npm run build","fuzz":"node bench/fuzz.ts"},"dependencies":{"fast-glob":"^3.3.2","oxc-parser":"^0.140.0","picocolors":"^1.1.1"},"devDependencies":{"@types/node":"^26.1.1","typescript":"^7.0.2"},"publishConfig":{"access":"public"},"_id":"@dhiravpatel/node-doctor@1.0.1","gitHead":"80750cb0c5550a611f54d0ab74801275e8240918","_nodeVersion":"22.23.1","_npmVersion":"10.9.8","dist":{"integrity":"sha512-cc1mv3i75GvZhhJoQ3BsRcHx8PRomJrq8dqHAiNh5YC/UlSgOfUr9Bd80px2WAePtCXGDC7aDfMHfEIRYd/87g==","shasum":"5eabfe966f69787565096d26478bea7503a4f8a1","tarball":"https://registry.npmjs.org/@dhiravpatel/node-doctor/-/node-doctor-1.0.1.tgz","fileCount":463,"unpackedSize":1822635,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@dhiravpatel%2fnode-doctor@1.0.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIBnD9kL8iLDb22XKrlagReGzUdnG0OqA77jKhYOasskWAiByHQuryBWQA8L8RuC81gyFYh6T6gmgAKIxFX24DCe33w=="}]},"_npmUser":{"name":"dhiravpatel","email":"dhiravpatel07@gmail.com"},"directories":{},"maintainers":[{"name":"dhiravpatel","email":"dhiravpatel07@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/node-doctor_1.0.1_1785499263061_0.26769492502029335"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-31T11:54:12.459Z","modified":"2026-07-31T12:01:05.326Z","1.0.0":"2026-07-31T11:54:12.764Z","1.0.1":"2026-07-31T12:01:03.209Z"},"bugs":{"url":"https://github.com/DhiravPatel/node_doctor/issues"},"author":{"name":"Dhirav Patel"},"license":"MIT","homepage":"https://node-doctor.vercel.app/","keywords":["node","static-analysis","linter","security","express","fastify","prisma","async","sast","code-review","ai","agent"],"repository":{"type":"git","url":"git+https://github.com/DhiravPatel/node_doctor.git"},"description":"Deterministic, offline-first static analysis for Node.js backends — catches the defects that compile, pass tests, and fail under concurrency.","maintainers":[{"name":"dhiravpatel","email":"dhiravpatel07@gmail.com"}],"readme":"<div align=\"center\">\n\n# node.doctor\n\n### Your agent writes bad Node. This catches it.\n\nDeterministic, offline-first static analysis for Node.js backends — built for the\nclass of defect that compiles, passes the tests, runs fine on your machine, and\nfalls over the moment two requests arrive at once.\n\n[![npm version](https://img.shields.io/npm/v/@dhiravpatel/node-doctor.svg)](https://www.npmjs.com/package/@dhiravpatel/node-doctor)\n[![CI](https://img.shields.io/github/actions/workflow/status/DhiravPatel/node_doctor/ci.yml?branch=main)](https://github.com/DhiravPatel/node_doctor/actions)\n[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](#license)\n[![Node](https://img.shields.io/node/v/@dhiravpatel/node-doctor.svg)](#requirements)\n\n```bash\nnpx @dhiravpatel/node-doctor@latest .\n```\n\n**[node-doctor.vercel.app](https://node-doctor.vercel.app/)** — browse all 133 diagnostics\n\n</div>\n\n---\n\n## What it is\n\nnode.doctor scans Node.js server code and reports the defects generic tooling\nmisses — the ones that are *correct in isolation and wrong under load*: an `async`\nExpress handler with no error path, a `readFileSync` on the request path, an N+1\nacross a loop, a `Promise.all` that opens a socket per row, injection and\nsecret-handling sinks.\n\nIt runs **133 diagnostics** — including a whole-tree scan for **committed secrets**\nin `.env`, config, CI, and key files — produces a transparent **0–100 health\nscore** entirely on your machine (no network, no telemetry), and can push the\nsame knowledge **upstream into your coding agent** as an installable skill and an\nMCP tool.\n\n## Quick start\n\n```bash\nnpx @dhiravpatel/node-doctor@latest .              # scan the current directory\nnpx @dhiravpatel/node-doctor@latest diagnostics    # list every diagnostic + gating\nnpx @dhiravpatel/node-doctor@latest . --json       # machine-readable report\n```\n\nTypical output on a codebase that needs help:\n\n```\n  node.doctor v0.1.0  checkout-service\n  148 files · 21,904 lines · 50/62 diagnostics active\n  detected: typescript esm express prisma jsonwebtoken\n\n  ██████░░░░░░░░░░░░░░░░░░░░░░░░  21/100  critical\n  38 errors · 17 warnings · 71.4 weighted/kLOC\n\n  Security (19)\n  ✖ SQL built by string interpolation · 6 sites\n     src/orders/repository.ts:88:24\n     → Use parameter binding: db.query(\"… WHERE id = $1\", [id])\n     node-doctor/no-sql-template-interpolation\n```\n\n## Why it's different\n\n- **Context-aware.** `readFileSync` at module scope is a config load; the same\n  call inside a handler stalls *every* concurrent request. node.doctor tells them\n  apart — most linters flag both (and get disabled) or neither (and miss the bug).\n- **Version-aware.** It reads your manifest and retires diagnostics that no longer\n  apply — the Express-4 async-handler footgun is silent on Express 5.\n- **Transparent, local score.** A 0–100 number from a published formula,\n  reproducible by hand, offline. No server call, no closed model.\n\n## Features\n\n- **133 diagnostics** across Security, Reliability, Bugs, Performance, and\n  Maintainability — each with a valid + invalid test; FP-prone ones are opt-in.\n- **Whole-tree secret scan** — committed credentials in `.env`, YAML/CI configs,\n  and `*.pem`/`*.key` files, gated to git-tracked files so a local `.env` is safe.\n- **Deploy-config analysis** — the same engine reads the files that ship your code:\n  **Dockerfiles** (final stage running as root, mutable base tags, secrets baked\n  into a layer), **Kubernetes manifests** (privileged containers, host namespaces,\n  missing resource limits), **GitHub Actions** (script injection via\n  `\\${{ github.event.* }}`, `pull_request_target` checking out untrusted code,\n  unpinned actions), and **Terraform/CloudFormation** (open security groups,\n  over-broad IAM, public buckets). Each demands positive evidence of the file type\n  first — a docker-compose file with `privileged: true` is not a Kubernetes finding.\n- **Cross-file call graph + interprocedural taint** — flags a blocking sink, or an\n  injection sink fed by request data, in a helper reached from a handler *through other\n  files*, and names the whole path. In a monorepo the graph **crosses package\n  boundaries**: a handler in `apps/api` reaching a blocking read in `packages/db`\n  is a finding, attributed to the package that contains the code.\n- **Runtime-aware** — detects Bun, Deno, and edge runtimes (Cloudflare Workers,\n  Vercel Edge) and gates diagnostics accordingly; `node:fs` on the edge is an\n  error there and silent everywhere else.\n- **Modernization score** (`node-doctor modernize`) — a second number, separate\n  from health, that goes *up* as you retire deprecated APIs and unsupported Node\n  majors.\n- **AI-feature security** — Node is where LLM apps are built, and their code has its\n  own vulnerability class. On a project that imports an AI SDK, node.doctor flags\n  **prompt injection** (request data welded into a `system` prompt — the same taint\n  engine as SQL injection, silent on the isolated `{ role: \"user\", content }`\n  shape), **LLM output reaching a dangerous sink** (`eval`/shell/SQL/HTML), an **MCP\n  tool that runs shell/SQL/`fs` on model-controlled arguments**, **system-prompt\n  leakage**, and **LLM calls in a loop**. The whole pack is silent on a project that\n  never calls a model.\n- **Type-aware diagnostics** (`--typed`) — an optional pass that reads the project's\n  own TypeScript types. It catches a **discarded promise** even when the callee is\n  typed `(): Promise<T>` rather than written `async` — the case a syntactic check\n  cannot see, and the majority in a real TypeScript codebase. The compiler is an\n  optional peer (`typescript@^5`); without it a normal scan is unchanged, and\n  `--typed` fails loudly rather than silently finding nothing.\n- **Framework, API and migration depth** — GraphQL/gRPC server-setup checks\n  (introspection on in production, insecure gRPC credentials), Hapi/Restify\n  diagnostics gated to those stacks, and SQL-migration checks (a destructive\n  statement outside a down section, `ADD COLUMN NOT NULL` with no default, an\n  unindexed foreign key).\n- **CODEOWNERS routing** (`--owners`) and a **PR risk score** (`--risk`) — findings\n  grouped by the team that owns them, plus one explainable number for triage.\n- **Exploitability proof** (`node-doctor paths`) — for every injection sink fed by\n  request data, the exact source→sink chain the taint engine resolved: request\n  handler → each helper → the `eval`/shell/SQL sink, with `file:line` at every hop.\n  Proof a finding is *reachable*, not a heuristic; exits 1 on a proven path.\n- **Change-impact / blast radius** (`node-doctor impact`) — from the import graph,\n  which routes and files a change reaches downstream. `impact --diff main` answers\n  \"what does my PR touch?\" before review: *your two-line change to `db/pool.ts` is\n  reachable from 14 routes.* Deterministic graph reachability, not a heuristic.\n- **Data access map** (`node-doctor data-map`) — the matrix of which routes touch\n  which database entities, and how (read/write/delete). It walks the call graph\n  forward from each route handler (cross-file), recognizes Prisma/TypeORM/Knex and\n  raw SQL — including `` $queryRaw`…` `` tagged templates and interpolated query\n  strings — and inverts the index so you can also ask *which endpoints write\n  `payments`?*. Conservative: an entity is emitted only when it can be proven from\n  source; a dynamic table is counted as unresolved, never guessed. Deterministic.\n- **Schema drift & dead data** (`node-doctor schema-drift`) — the Prisma schema\n  crossed against every statically-visible model access, in both directions: code\n  referencing a field the schema does not define (a runtime validation error found\n  at build time, with a did-you-mean suggestion) and schema models nothing touches\n  (migration debt and compliance surface). Operators, relation traversals, and\n  compound unique keys are understood; a spread silences the object; dead-model\n  claims are made only when no dynamic access or unresolved raw SQL could hide a\n  use. Exits 1 on drift.\n- **Architecture analysis** (`node-doctor architecture`) — import cycles found\n  exactly from the graph (a runtime hazard under ESM: partially-initialized\n  imports, `undefined` at module scope, TDZ errors that appear only when the\n  evaluation order flips), plus layer violations (a service importing back up\n  into routes, a route reaching past the service layer into a repository) and\n  hub modules. Layer claims fire only when both files sit in an unambiguous\n  layer directory, so an unlayered project gets zero noise. Cycles exit 1.\n- **OpenAPI spec generation** (`node-doctor openapi`) — an OpenAPI 3.1 document\n  derived from the routes themselves, so the docs cannot drift from the code that\n  serves them. Path params, query params mined from the handler, request-body\n  presence, response codes from `res.status(…)`, and security from the auth\n  middleware chain. It asserts only what it can prove — a body is a free-form\n  object rather than an invented schema, and a route whose path is not static is\n  skipped and reported rather than guessed at. Deterministic, so it can be\n  committed and diffed in CI.\n- **Package API semver lint** (`node-doctor semver`) — semver for your internal\n  package exports. Snapshots every workspace package's export surface, then on\n  each run diffs it: a removed export is breaking and fails the build unless the\n  package's version bumped major (0.x minor counts, per semver); additions get a\n  \"minor expected\" advisory. A surface it cannot fully prove (an unfollowable\n  `export *`, an opaque `module.exports`) never yields a removal claim.\n- **Queue & topic topology** (`node-doctor queues`) — the event-driven equivalent\n  of the import graph: who publishes to each Kafka topic / Rabbit queue / BullMQ\n  queue / NATS subject, who consumes it, and what falls out — orphan topics\n  (published, never consumed) and dead consumers (subscribed, nothing publishes).\n  Every receiver is traced to a client binding constructed from the library's own\n  entry point (an EventEmitter's `.publish` never counts), topics come only from\n  static strings, a dynamic topic suppresses exactly the claims it could hide, and\n  a same-file re-enqueue loop is shown as info, never judged.\n- **Observability coverage** (`node-doctor observability`) — the observability\n  equivalent of test coverage. It scores each route's handler on whether an async\n  failure path is handled, whether failures actually log (a swallowing\n  `catch`/`.catch(() => {})` fails), whether outbound calls are timed, and whether\n  logs carry a correlation id — then reports a per-route and codebase score. Answers\n  \"could you debug this route at 3am from the logs alone?\", a number nobody measures.\n- **Agent context hygiene** (`node-doctor context`) — a new privacy surface that\n  exists only because agents read your repo. It finds the on-disk files an AI agent\n  must never load into context — `.env` files, private keys, credential files, DB\n  dumps, and configs with an embedded provider key — reports which are not yet\n  fenced off, and with `--write` generates the ignore artifacts that keep them out\n  (`.aiignore`, `.cursorignore`, and Claude Code `Read()` deny rules). Source code\n  is never flagged — an agent is *supposed* to read your code. Deterministic and\n  idempotent: re-running reproduces byte-identical artifacts.\n- **CI baseline delta** — reports only the findings your PR introduced.\n- **`deslop`** dead-code scan — unused files, exports, and dependencies.\n- **MCP server** — call node.doctor as a native tool from any MCP client.\n- **Language server** (`node-doctor lsp`) + a VS Code extension — inline diagnostics,\n  hover, and quick fixes on the unsaved buffer, from the same engine as the CLI.\n- **Autofix** (`--fix`), self-contained **HTML report** (`--html-out`),\n  **content-hash cache** (`--cache`), and **watch mode** (`--watch`).\n- **Config file** + **inline suppression** with mandatory reasons.\n- **ESLint adapter**, a stable **programmatic API**, and JSON / SARIF 2.1.0 /\n  GitHub-annotation output.\n- **Offline & deterministic** — no network calls, byte-identical runs.\n\n## Diagnostics at a glance\n\n| Category | Focus | Score weight | Count |\n| --- | --- | --- | --- |\n| **Security** | Injection, secrets, auth, deserialization, GraphQL/gRPC + AI-feature security, committed-secret + IaC/container/CI scan | 2.0 | 54 |\n| **Reliability** | Crashes, hangs, lifecycle, runtime portability, deploy config, migrations, env drift | 1.5 | 28 |\n| **Bugs** | Logic errors, wrong results, dead routes | 1.5 | 10 |\n| **Performance** | Event-loop stalls, N+1, AI cost | 1.0 | 11 |\n| **Maintainability** | Structure, hygiene, dead code, complexity, deprecated APIs | 0.5 | 10 |\n\nRun `node-doctor diagnostics` for the full catalog with gating.\n\n## Fix with an AI agent\n\nFound the bugs — now hand them to the agent that can fix them. `node-doctor fix`\nscans, then offers to pass every finding straight to a coding agent (Claude Code,\nCodex, or Cursor) with a precise, root-cause-first instruction prompt:\n\n```bash\nnpx @dhiravpatel/node-doctor@latest fix .\n```\n\n```\n  38 findings · 21/100 critical\n\n  What would you like to do?\n    1) Fix with Claude Code  (claude)\n    c) Copy the prompt to your clipboard\n    p) Print the prompt\n    s) Skip\n  > 1\n\n  → Handing 38 finding(s) to Claude Code. It runs in auto-accept mode and will\n    fix them end-to-end, then re-scan to confirm.\n```\n\nThe prompt groups findings by root cause, names the exact fix for each, and tells\nthe agent to **fix the cause — never suppress** — then re-run node.doctor to\nverify. It only lists agents actually installed on your machine; on a\nnon-interactive shell (or with `--print`) it prints the prompt instead of\nlaunching anything.\n\n```bash\nnode-doctor fix .            # scan, then pick an agent from the menu\nnode-doctor fix . --yes      # skip the menu, launch the first available agent\nnode-doctor fix . --agent claude   # choose the agent explicitly\nnode-doctor fix . --review   # agent asks before each edit (no auto-accept)\nnode-doctor fix . --print    # just print the prompt for your own agent\n```\n\n## Command-line\n\n```\nnode-doctor [directory] [options]\nnode-doctor fix [directory]                     scan, then hand findings to an AI agent\nnode-doctor diagnostics [--json] [filters]      list diagnostics + effective severity/source\nnode-doctor diagnostics set|enable|disable <id> <sev>   edit the config in place\nnode-doctor diagnostics category <c> <sev> | ignore-tag <t>\nnode-doctor delta --baseline <f> --current <f>  report only introduced findings\nnode-doctor deslop [directory]                  dead-code scan\nnode-doctor explain <id> | <file>:<line>        why a diagnostic fired (with a code frame)\nnode-doctor install [--client <name>]           install the agent skill\nnode-doctor install --git-hook                  install an advisory pre-commit hook\nnode-doctor ci                                  scaffold a GitHub Actions workflow\nnode-doctor conventions [dir]                   write CLAUDE.md/AGENTS.md from your stack\nnode-doctor ratchet init|check                  lock current debt; fail only on new findings\nnode-doctor surface [--baseline <f>]            map routes + auth posture; diff for breaking changes\nnode-doctor observability [dir]                 per-route \"could you debug this at 3am?\" coverage score\nnode-doctor impact <files…> | --diff [base]     blast radius: what routes/files a change reaches\nnode-doctor data-map [directory]                which routes touch which DB entities, and how (read/write/delete)\nnode-doctor schema-drift [directory]            Prisma schema vs code: unknown-field drift + dead models\nnode-doctor queues [directory]                  queue/topic topology: publishers, consumers, orphans\nnode-doctor semver [--baseline <f>]             package-export surface; lint version bumps against it\nnode-doctor openapi [directory]                 generate an OpenAPI 3.1 spec from the actual routes\nnode-doctor architecture [directory]            import cycles, layer violations, hub modules\nnode-doctor paths [directory]                   source→sink attack paths (exploitability proof)\nnode-doctor context [dir] [--write]             find files an AI agent must not read; --write fences them off\nnode-doctor sbom [--framework spdx]             CycloneDX / SPDX bill of materials\nnode-doctor modernize [directory]               modernization score: deprecated APIs + Node major\nnode-doctor mcp                                 run as an MCP server\nnode-doctor lsp                                 run as a language server (editors)\nnode-doctor init                                scaffold a config\nnode-doctor version                             version + platform + Node runtime\n\nOutput   --json · --json-compact · --score · --json-out · --sarif-out · --html-out\n         --md-out · --annotations · --color / --no-color\nScan     --fix · --fix-diff (emit autofixes as a patch) · --history (git-history secrets) · --dead-code · --cache · --watch · --audit · --max-duration <sec>\n         --typed (type-aware diagnostics; needs typescript@^5 in the project)\n         --no-parallel (analyze files serially; default is a concurrency pool)\nScope    --only <glob> · --diff [base] · --staged · --scope <lines|files>\n         --changed-files-from <f> · --include-untracked\nMonorepo --project <name|path> (repeatable) · --no-workspaces\nGate     --blocking <error|warning|none>\nDisplay  --category <c> (repeatable) · --no-warnings · --verbose\n         --owners (group findings by CODEOWNERS team) · --risk (PR risk score, with --diff)\nConfig   --config <path> · --ignore-tag <tag>\nFix      --yes,-y · --agent <claude|codex|cursor> · --print · --review · --verify\n```\n\nExit codes: `0` no blocking findings · `1` blocking findings · `2` tool error.\n\n**Config** lives in `node-doctor.config.json` (or `.jsonc`/`.js`, or a `nodeDoctor`\nkey in `package.json`), resolved by walking up to the repo root. A generated\n[JSON Schema](./schema/node-doctor.config.schema.json) gives editors autocomplete.\nPer-path `overrides` re-severity or disable diagnostics for a glob; `rootDir`\nredirects the scan.\n\n**Monorepos** are detected automatically: point node.doctor at a workspace root\n(npm/yarn/bun `workspaces` or `pnpm-workspace.yaml`) and it scans every member\npackage, scores each separately, and reports a worst-of health score. Each\nmember's config layers over the root config. Use `--project <name|path>` to scan\none, or `--no-workspaces` to treat the root as a single project.\n\n## Continuous integration\n\nThe **baseline delta** makes node.doctor adoptable on a legacy codebase from day\none: scan the base branch, scan the head branch, and report only the difference.\nMatching is **evidence-based** (diagnostic + message + the triggering code), so\nmoving a finding to a new line or file doesn't read as newly introduced — only a\ngenuinely new defect fails the check.\n\nScaffold the whole thing in one command:\n\n```bash\nnpx @dhiravpatel/node-doctor@latest ci        # writes .github/workflows/node-doctor.yml\n```\n\nThe bundled [GitHub Action](./.github/action.yml) runs the baseline delta and, on\na pull request, **upserts a summary comment**, posts **inline review comments** on\nthe changed lines, and publishes a **commit status** with the health score. Or\nwire it by hand:\n\n```yaml\n- run: |\n    git checkout origin/$BASE\n    npx @dhiravpatel/node-doctor@latest . --json-out base.json --blocking none\n    git checkout $SHA\n    npx @dhiravpatel/node-doctor@latest . --json-out head.json --blocking none\n    npx @dhiravpatel/node-doctor@latest delta --baseline base.json --current head.json --blocking error\n```\n\nFor local enforcement, `node-doctor install --git-hook` writes an advisory\npre-commit hook that scans staged files.\n\n## Agent integration\n\nPush node.doctor's knowledge into the agent that writes the code:\n\n```bash\nnpx @dhiravpatel/node-doctor@latest install         # skill → detected clients (Claude Code, Cursor, …)\nnpx @dhiravpatel/node-doctor@latest install --skill improve-node   # a read-only audit-then-plan skill\nnpx @dhiravpatel/node-doctor@latest install --agent-hooks          # post-edit hooks (feedback as it edits)\nnpx @dhiravpatel/node-doctor@latest mcp             # …or run as an MCP server\n```\n\n```json\n{ \"mcpServers\": { \"node-doctor\": { \"command\": \"npx\", \"args\": [\"node-doctor\", \"mcp\"] } } }\n```\n\nSix MCP tools: `scan`, `scan_diff`, `diagnostics`, `explain`, `deslop`, and **`check_snippet`** —\nwhich lints a fragment *before* the agent writes it to disk, the cheapest feedback loop available.\n\nEvery finding carries a **confidence** (`high`/`medium`/`low`) so an agent can auto-fix what is\ncertain and escalate what is not, and every report carries a **provenance** record (tool version,\nruleset hash, config hash) so a CI result is reproducible and explainable.\n\n## Programmatic API\n\n```js\nimport { diagnose } from \"node-doctor\";\n\nconst report = await diagnose(\"./service\");                  // one project\nconsole.log(report.score.score, report.score.label, report.findings.length);\n\nconst batch = await diagnose({ directories: [\"a\", \"b\"] });   // many, resilient\nconsole.log(batch.ok, batch.score.score);                    // worst-of aggregate\n```\n\nExports include `diagnose`, `scanProject`, `scanWorkspaces`, `lintSource`,\n`computeDelta`, `calculateScore`, `renderReport`/`renderReportMarkdown`,\n`runDeslop`, `runTextScan`, `DIAGNOSTICS`, and `DIAGNOSTICS_BY_ID`. node.doctor\nalso ships as an **ESLint plugin** (`node-doctor/eslint`) and an **oxlint plugin**\n(`node-doctor/oxlint`) — both re-run the same engine over file-scope diagnostics.\n\n## Requirements\n\n- **Node.js ≥ 20.19** to run the published package (compiled ESM, zero cold-start\n  transpile cost).\n- Analyzes `.js` `.mjs` `.cjs` `.ts` `.mts` `.cts` `.jsx` `.tsx`. TypeScript is\n  parsed structurally — no `tsconfig` resolution or type checking required.\n\n## Contributing\n\nDiagnostics are one file each; every one ships with a valid + invalid test and\nmust never fire on the `good-app` canary. See [CONTRIBUTING.md](./CONTRIBUTING.md).\n\n## License\n\n[MIT](./LICENSE)\n","readmeFilename":"README.md"}