setup-node's registry-url writes an .npmrc with _authToken placeholder
that short-circuits the OIDC exchange. Also need npm 11.5.1+ for
trusted publishing support.
- Single object arg (`DiagnoseProjectsInput`) instead of two positional args
- "Projects" vocabulary matching the rest of the codebase
- Per-project scan options (`deadCode`, `lint`, etc.) flat on the entry
instead of nested in `config.deadCode`
- `config` on a project entry means `ReactDoctorConfig` directly
- Discriminated union `ProjectResult` (`ok: true | false`) instead of
separate `modules` + `errors` arrays
The React Compiler rules (`react-hooks-js/*`) were silently missing in
published builds because `resolveReactHooksJsPlugin` resolved
`eslint-plugin-react-hooks` via `createRequire(import.meta.url)` — which
found the package in the monorepo's devDependencies but not in the
npm-installed package where it was only an optional peer dep.
Move `eslint-plugin-react-hooks` from optional peerDependencies +
devDependencies to a real dependency in both `@react-doctor/core` and
`react-doctor` so the compiler rules fire automatically for any project
with `babel-plugin-react-compiler` detected.
Updated the version of the react-doctor skill from 1.1.0 to 1.1.1. The playbook description has been refined for clarity, emphasizing its role as the single source of truth in the triage process. No functional changes were made to the skill's operation.
The skill body now points the agent at https://www.react.doctor/prompts/react-doctor-agent.md
when the user types `/doctor` or asks for a full triage. The remote playbook is the
single source of truth (same scan -> filter -> triage -> fix -> validate loop as the
daily GitHub Actions bot, adapted for local use), so prompt edits propagate to every
installed agent on the next invocation without re-publishing the skill.
Bumps SKILL.md version 1.0.0 -> 1.1.0.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(cli): suppress CI and agent interactivity
Use a shared CI-or-agent environment check to skip core progress spinners and optional setup prompts in automated React Doctor runs.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(tests): isolate setup prompt CI env
Clear the same CI and coding-agent environment signals used by the prompt guard so setup prompt tests remain deterministic in CI.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(ci): point smoke test at core fixture
Co-authored-by: Ray Arayilakath <me@rayhanadev.com>
* style: format JSON smoke fixture path
Co-authored-by: Ray Arayilakath <me@rayhanadev.com>
---------
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Both helpers built identical ProjectInfo shapes inline. Moved the
omitted-vs-explicit-null reactMajorVersion distinction into
buildTestProject so collectRuleHits can just delegate.
Co-authored-by: Aiden Bai <aidenybai@users.noreply.github.com>
Lint flagged it as no-unused-vars — left over from when the local
flattenMemberName helper used the type directly. After aliasing to
the canonical flattenCalleeName, the type-only import is dead.
Behaviour-neutral.
Co-authored-by: Aiden Bai <aidenybai@users.noreply.github.com>
After the canonical helper landed in `utils/is-inline-function-expression.ts`,
`event-handler-reference.ts` kept a re-export so collect-function-like-
local-names could keep its existing import path. Migrate that single
remaining consumer to import the canonical location directly, then
drop the transitional re-export.
`event-handler-reference.ts` still imports the helper internally
(used by isEventHandlerValue) so the import line stays — just no
longer fronted as a public surface of this module.
Behaviour-neutral.
Co-authored-by: Aiden Bai <aidenybai@users.noreply.github.com>
Format drift in 6 files touched by d09a994c (isInlineFunctionExpression
promotion + 7 rule-file applications). Reformat is behaviour-neutral.
Co-authored-by: Aiden Bai <aidenybai@users.noreply.github.com>
`isInlineFunctionExpression` (the 2-type `ArrowFunctionExpression`
or `FunctionExpression` predicate, used for expression-position
checks where a FunctionDeclaration can't appear) was tucked inside
`rules/state-and-effects/utils/event-handler-reference.ts`. Promote
to `utils/is-inline-function-expression.ts` so all rule files can
use one canonical type guard.
Apply to seven new sites that previously inlined the 2-type triplet:
- architecture/no-many-boolean-props.ts (VariableDeclarator.init check)
- architecture/react-compiler-destructure-method.ts (initializer check)
- correctness/no-prevent-default.ts (JSX prop expression check)
- correctness/no-uncontrolled-input.ts (VariableDeclarator.init check)
- js-performance/async-await-in-loop.ts (replaces local
isFunctionishExpression with the canonical helper)
- js-performance/js-length-check-first.ts (CallExpression callback check)
- nextjs/nextjs-async-client-component.ts (VariableDeclarator.init check)
The 2-type pattern is intentionally narrower than isFunctionLike
because declarations only appear as statements — using the wider
`isFunctionLike` at these expression-position call sites would be
incorrect (and even if it weren't, the type-narrow wouldn't match).
event-handler-reference.ts re-exports the helper so its existing
intra-directory consumer keeps working without an import path change.
Behaviour-neutral; -28 lines net across the 7 files.
Co-authored-by: Aiden Bai <aidenybai@users.noreply.github.com>
Sweep across rule files where the inline triplet
`isNodeOfType(x, 'FunctionDeclaration') || ... || ...ArrowFunctionExpression`
was used as a pure boundary-check (not part of a richer predicate):
- architecture/no-legacy-context-api.ts
- js-performance/async-await-in-loop.ts (inside walkAst visitor)
- js-performance/js-batch-dom-css.ts
- js-performance/js-hoist-intl.ts
- nextjs/nextjs-no-side-effect-in-get-handler.ts
- performance/async-defer-await.ts (two sites — positive + negated)
Each site now imports the canonical isFunctionLike type guard.
Files where the triplet check is intentionally selective (e.g.
no-unstable-nested-components includes ClassDeclaration / ClassExpression,
inferFunctionLikeName in no-unstable-nested-components only matches
FunctionDeclaration + FunctionExpression for named-function lookup,
react-compiler-destructure-method uses a 2-type ArrowFunction/FunctionExpression
check for an initializer position) are left as-is.
Behaviour-neutral; ~30 lines net removed across the 6 files.
Co-authored-by: Aiden Bai <aidenybai@users.noreply.github.com>
Two adjacent function-boundary checks (each constructed the same
isCrossingFunctionBoundary local from the 3-type triplet) collapse
to direct isFunctionLike(current) calls.
Behaviour-neutral; -10 lines net.
Co-authored-by: Aiden Bai <aidenybai@users.noreply.github.com>
The walker that walks up the AST to find an enclosing component
inlined the 3-check (FunctionDeclaration / FunctionExpression /
ArrowFunctionExpression) pattern. Replace with the canonical
`isFunctionLike` type guard.
The other 3-check at `inferFunctionLikeName` is NOT replaced — that
one is specifically about "has a named function id" and only
matches FunctionDeclaration + FunctionExpression (excludes
ArrowFunctionExpression). Different semantic; left alone.
Behaviour-neutral; -4 lines net.
Co-authored-by: Aiden Bai <aidenybai@users.noreply.github.com>
The local NESTED_FUNCTION_TYPES set (FunctionDeclaration /
FunctionExpression / ArrowFunctionExpression) was the same 3-element
set already centralised as FUNCTION_LIKE_TYPES in constants/js.ts.
Drop the local copy; import the canonical version.
Behaviour-neutral; -5 lines net.
Co-authored-by: Aiden Bai <aidenybai@users.noreply.github.com>