react-doctor 0.9.3-dev.0b673cc → 0.9.3-dev.8a22de1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -1,5 +1,5 @@
1
1
 
2
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="1a34c548-5d3e-559c-8a3a-8a869ca2847a")}catch(e){}}();
2
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="e4075e6f-4128-5af6-bc97-656272450282")}catch(e){}}();
3
3
  import { createRequire } from "node:module";
4
4
  import * as NodeChildProcess from "node:child_process";
5
5
  import { execFile, execFileSync, spawn, spawnSync } from "node:child_process";
@@ -78911,7 +78911,7 @@ const BASELINE_FILES_TEMP_DIR_PREFIX = "react-doctor-baseline-";
78911
78911
  const SCAN_RESULT_CACHE_FILENAME = "scan-cache.json";
78912
78912
  const RUN_GIT_MAX_BUFFER_BYTES = 64 * 1024 * 1024;
78913
78913
  const GH_DEFAULT_BRANCH_PROBE_TIMEOUT_MS = 5e3;
78914
- const TUI_REPORT_COMPACT_MAX_ROWS = 29;
78914
+ const TUI_REPORT_COMPACT_MAX_ROWS = 23;
78915
78915
  const TUI_REPORT_DETAIL_WIDTH_FRACTION = .6;
78916
78916
  const CI_TRUST_COMPANIES = "PayPal, Rippling, and Alibaba";
78917
78917
  const WELCOME_EXPLANATION_HOLD_MS = 1e3;
@@ -78962,9 +78962,12 @@ const METRIC = {
78962
78962
  lspScanCompleted: "lsp.scan.completed",
78963
78963
  lspScanDuration: "lsp.scan.duration",
78964
78964
  lspScanDiagnostics: "lsp.scan.diagnostics",
78965
- tuiActionMenuOpened: "tui.action_menu_opened",
78966
78965
  tuiCompactReportShown: "tui.compact_report_shown",
78967
78966
  tuiFindingNavigated: "tui.finding_navigated",
78967
+ tuiIssueStreamShown: "tui.issue_stream_shown",
78968
+ tuiProjectPathContextShown: "tui.project_path_context_shown",
78969
+ tuiReportActionSelected: "tui.report_action_selected",
78970
+ tuiCancelled: "tui.cancelled",
78968
78971
  tuiStackedReportCapped: "tui.stacked_report_capped",
78969
78972
  aiTrainingWarningShown: "ai.training.warning_shown",
78970
78973
  jsonOutUsed: "json.out_used"
@@ -79010,7 +79013,7 @@ const makeNoopConsole = () => ({
79010
79013
  });
79011
79014
  //#endregion
79012
79015
  //#region src/cli/utils/version.ts
79013
- const VERSION = "0.9.3-dev.0b673cc";
79016
+ const VERSION = "0.9.3-dev.8a22de1";
79014
79017
  //#endregion
79015
79018
  //#region src/cli/utils/json-mode.ts
79016
79019
  let context = null;
@@ -79368,13 +79371,13 @@ const isDevVersion = (version) => version === "0.0.0" || version.includes("-");
79368
79371
  * uploads source-map artifacts under, so stack frames symbolicate. Honors the
79369
79372
  * standard `SENTRY_RELEASE` override.
79370
79373
  */
79371
- const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.9.3-dev.0b673cc`;
79374
+ const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.9.3-dev.8a22de1`;
79372
79375
  /**
79373
79376
  * Deployment environment shown in Sentry's environment filter. Defaults to
79374
79377
  * `production` for tagged releases and `development` for dev/unbuilt versions,
79375
79378
  * overridable via the standard `SENTRY_ENVIRONMENT` env var.
79376
79379
  */
79377
- const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.9.3-dev.0b673cc") ? "development" : "production");
79380
+ const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.9.3-dev.8a22de1") ? "development" : "production");
79378
79381
  /**
79379
79382
  * Performance-tracing sample rate in `[0, 1]`. Reads `SENTRY_TRACES_SAMPLE_RATE`
79380
79383
  * (set to `0` to disable tracing) and falls back to
@@ -92535,6 +92538,14 @@ const buildNoScoreMessage = (isScoreDisabled, disabledMessage) => {
92535
92538
  return `${isScoreDisabled ? disabledMessage ?? "Score disabled by --no-score." : "Score unavailable (could not reach the score API)."} ${ENTERPRISE_CONTACT_HINT}`;
92536
92539
  };
92537
92540
  //#endregion
92541
+ //#region src/cli/utils/build-empty-report-message.ts
92542
+ const buildEmptyReportMessage = (input) => {
92543
+ const categoryFilters = [...input.categoryFilters];
92544
+ if (categoryFilters.length > 0) return `No issues found in category ${categoryFilters.join(", ")}!`;
92545
+ if (input.demotedDiagnosticCount > 0) return `No issues found! (${input.demotedDiagnosticCount} demoted from the ${input.outputSurface} surface — see config.surfaces.)`;
92546
+ return "No issues found!";
92547
+ };
92548
+ //#endregion
92538
92549
  //#region src/cli/utils/render-agent-guidance.ts
92539
92550
  const AGENT_GUIDANCE_LINES = [
92540
92551
  "Treat React Doctor diagnostics as starting hypotheses. Read the relevant code before confirming or suppressing each finding.",
@@ -93495,6 +93506,26 @@ const printNoScoreHeader = (noScoreMessage) => gen(function* () {
93495
93506
  yield* log("");
93496
93507
  });
93497
93508
  //#endregion
93509
+ //#region src/cli/utils/build-footer-description-lines.ts
93510
+ const FOOTER_DESCRIPTION_INDENT = " ";
93511
+ const buildFooterDescriptionLines = (description) => {
93512
+ return wrapTextToWidth(description, resolveMeasureWidth(2)).map((line) => highlighter.dim(`${FOOTER_DESCRIPTION_INDENT}${line}`));
93513
+ };
93514
+ //#endregion
93515
+ //#region src/cli/utils/build-footer-link-lines.ts
93516
+ const buildFooterLinkLines = ({ shareUrl }) => [
93517
+ ...shareUrl === null ? [] : [
93518
+ ` ${highlighter.bold("Share:")} ${highlighter.info(shareUrl)}`,
93519
+ ...buildFooterDescriptionLines("Tell others how you did on socials"),
93520
+ ""
93521
+ ],
93522
+ ` ${highlighter.bold("Docs:")} ${highlighter.info(DOCS_URL)}`,
93523
+ ...buildFooterDescriptionLines("Learn more about fixing issues, setting up CI/CD, and configuring rules with a config file"),
93524
+ "",
93525
+ ` ${highlighter.bold("GitHub:")} ${highlighter.info(CANONICAL_GITHUB_URL)}`,
93526
+ ...buildFooterDescriptionLines("Report issues and star the repository!")
93527
+ ];
93528
+ //#endregion
93498
93529
  //#region src/cli/utils/write-diagnostics-directory.ts
93499
93530
  const ruleDumpFileName = (ruleKey) => ruleKey.replace(/\//g, "--") + ".txt";
93500
93531
  const readPreviousRuleDumpFileNames = (directory) => {
@@ -93515,11 +93546,6 @@ const writeDiagnosticsDirectory = (diagnostics, outputDirectory) => {
93515
93546
  };
93516
93547
  //#endregion
93517
93548
  //#region src/cli/utils/render-summary.ts
93518
- const FOOTER_DESCRIPTION_INDENT = " ";
93519
- const printFooterDescription = (description) => gen(function* () {
93520
- const wrapWidth = resolveMeasureWidth(2);
93521
- for (const line of wrapTextToWidth(description, wrapWidth)) yield* log(highlighter.dim(`${FOOTER_DESCRIPTION_INDENT}${line}`));
93522
- });
93523
93549
  const buildShareUrl = (diagnostics, scoreResult, projectName) => {
93524
93550
  const errorCount = diagnostics.filter((diagnostic) => diagnostic.severity === "error").length;
93525
93551
  const warningCount = diagnostics.filter((diagnostic) => diagnostic.severity === "warning").length;
@@ -93536,17 +93562,8 @@ const printFooter = (input) => gen(function* () {
93536
93562
  yield* log("");
93537
93563
  yield* log(buildSectionDivider());
93538
93564
  yield* log("");
93539
- if (!input.isOffline) {
93540
- const shareUrl = buildShareUrl(input.diagnostics, input.scoreResult, input.projectName);
93541
- yield* log(` ${highlighter.bold("Share:")} ${highlighter.info(shareUrl)}`);
93542
- yield* printFooterDescription("Tell others how you did on socials");
93543
- yield* log("");
93544
- }
93545
- yield* log(` ${highlighter.bold("Docs:")} ${highlighter.info(DOCS_URL)}`);
93546
- yield* printFooterDescription("Learn more about fixing issues, setting up CI/CD, and configuring rules with a config file");
93547
- yield* log("");
93548
- yield* log(` ${highlighter.bold("GitHub:")} ${highlighter.info(CANONICAL_GITHUB_URL)}`);
93549
- yield* printFooterDescription("Report issues and star the repository!");
93565
+ const shareUrl = input.isOffline ? null : buildShareUrl(input.diagnostics, input.scoreResult, input.projectName);
93566
+ for (const line of buildFooterLinkLines({ shareUrl })) yield* log(line);
93550
93567
  });
93551
93568
  const printDiagnosticsDump = (diagnostics, outputDirectory, verbose, stream = "stdout") => gen(function* () {
93552
93569
  const writtenDirectory = yield* try_({
@@ -93655,7 +93672,6 @@ const recordOnboardingCompletion = (options) => {
93655
93672
  isNonInteractiveEnvironment: options.isNonInteractiveEnvironment
93656
93673
  })) markOnboardingComplete();
93657
93674
  };
93658
- const formatCategorySelection = (categoryFilters) => [...categoryFilters].join(", ");
93659
93675
  const buildChangedLineMatcher = (directory, changedLineRanges) => {
93660
93676
  const rangesByFile = /* @__PURE__ */ new Map();
93661
93677
  for (const entry of changedLineRanges) rangesByFile.set(toForwardSlashes(entry.file), entry.ranges);
@@ -94183,9 +94199,11 @@ const finalizeAndRender = (input) => gen(function* () {
94183
94199
  if (hasSkippedChecks) {
94184
94200
  const skippedLabel = skippedChecks.join(" and ");
94185
94201
  yield* warn$1(highlighter.warn(`No issues detected, but ${skippedLabel} checks failed — results are incomplete.`));
94186
- } else if (options.categoryFilters.size > 0) yield* log(highlighter.success(`No issues found in category ${formatCategorySelection(options.categoryFilters)}!`));
94187
- else if (demotedDiagnosticCount > 0) yield* log(highlighter.success(`No issues found! (${demotedDiagnosticCount} demoted from the ${options.outputSurface} surface — see config.surfaces.)`));
94188
- else yield* log(highlighter.success("No issues found!"));
94202
+ } else yield* log(highlighter.success(buildEmptyReportMessage({
94203
+ categoryFilters: options.categoryFilters,
94204
+ demotedDiagnosticCount,
94205
+ outputSurface: options.outputSurface
94206
+ })));
94189
94207
  yield* log("");
94190
94208
  yield* pause;
94191
94209
  if (hasSkippedChecks) {
@@ -94276,7 +94294,7 @@ const buildHandoffPayload = (input) => {
94276
94294
  try {
94277
94295
  outputDirectory = writeDiagnosticsDirectory([...input.diagnostics], input.outputDirectory);
94278
94296
  } catch {}
94279
- const lines = [`Fix the top ${topGroups.length} React Doctor ${topGroups.length === 1 ? "issue" : "issues"} in ${input.projectName} on this pass — leave the rest for a follow-up.`, ""];
94297
+ const lines = [`Review and fix the top ${topGroups.length} React Doctor ${topGroups.length === 1 ? "finding" : "findings"} in ${input.projectName}. Leave the rest for a follow-up.`, ""];
94280
94298
  topGroups.forEach(([ruleKey, ruleDiagnostics], index) => {
94281
94299
  const representative = ruleDiagnostics[0];
94282
94300
  const severityLabel = representative.severity === "error" ? "ERROR" : "WARN";
@@ -94304,7 +94322,7 @@ const buildHandoffPayload = (input) => {
94304
94322
  const ruleSummaries = deferredMigrationBuckets.map((bucket) => `${bucket.title} (${bucket.fileCount} files)`).join(", ");
94305
94323
  lines.push(`Some of the rest are migration-scale (span dozens of files): ${ruleSummaries}. For each, fix a representative sample, confirm the recipe holds, and get the code owner's sign-off before changing the rest in one pass.`, "");
94306
94324
  }
94307
- lines.push("Then work through the rest from the full results above.");
94325
+ lines.push("Stop after this pass. Summarize the remaining findings for a follow-up.");
94308
94326
  return lines.join("\n");
94309
94327
  };
94310
94328
  //#endregion
@@ -94514,20 +94532,20 @@ const resolveOpenCommand = (url) => {
94514
94532
  args: [url]
94515
94533
  };
94516
94534
  };
94517
- const openUrl = (url) => {
94535
+ const openUrl = (url) => new Promise((resolve) => {
94518
94536
  try {
94519
94537
  const { command, args } = resolveOpenCommand(url);
94520
94538
  const child = spawn(command, args, {
94521
94539
  detached: true,
94522
94540
  stdio: "ignore"
94523
94541
  });
94524
- child.on("error", () => {});
94542
+ child.once("spawn", () => resolve(true));
94543
+ child.once("error", () => resolve(false));
94525
94544
  child.unref();
94526
- return true;
94527
94545
  } catch {
94528
- return false;
94546
+ resolve(false);
94529
94547
  }
94530
- };
94548
+ });
94531
94549
  //#endregion
94532
94550
  //#region src/cli/utils/ask-add-to-github-actions.ts
94533
94551
  const CI_YES_CHOICE = "ci-yes";
@@ -94568,7 +94586,7 @@ const askAddToGitHubActions = async (prompt = prompts) => {
94568
94586
  if (ciChoice === void 0) return "cancel";
94569
94587
  if (ciChoice === CI_YES_CHOICE) return "yes";
94570
94588
  if (ciChoice === CI_NO_CHOICE) return "no";
94571
- const opened = openUrl(GITHUB_ACTIONS_SETUP_URL);
94589
+ const opened = await openUrl(GITHUB_ACTIONS_SETUP_URL);
94572
94590
  cliLogger.log(opened ? `Opened ${highlighter.info(GITHUB_ACTIONS_SETUP_URL)} in your browser.` : `Visit ${highlighter.info(GITHUB_ACTIONS_SETUP_URL)} to learn more.`);
94573
94591
  cliLogger.break();
94574
94592
  }
@@ -97261,6 +97279,55 @@ const installAction = async (options, command) => {
97261
97279
  }
97262
97280
  };
97263
97281
  //#endregion
97282
+ //#region src/cli/utils/should-use-tui.ts
97283
+ const shouldUseTui = (input) => {
97284
+ if (input.isNonInteractiveEnvironment || !input.stdinIsTty || !input.stdoutIsTty || !input.supportsRawMode || input.nodeMajorVersion < 22 || input.terminalName === "dumb") return false;
97285
+ const flags = input.flags;
97286
+ let requiresStableConfiguredScope = false;
97287
+ if (input.userConfig?.scope !== void 0) requiresStableConfiguredScope = input.userConfig.scope !== "full";
97288
+ else if (input.userConfig?.diff !== void 0) requiresStableConfiguredScope = input.userConfig.diff !== false && input.userConfig.diff !== "false";
97289
+ else if (input.userConfig?.base !== void 0) requiresStableConfiguredScope = true;
97290
+ return !(flags.verbose === true || flags.outputDir !== void 0 || flags.score === true || flags.json === true || flags.jsonCompact === true || flags.jsonOut !== void 0 || flags.staged === true || flags.scope !== void 0 || flags.base !== void 0 || flags.includeUntracked === true || flags.diff !== void 0 || flags.changedFilesFrom !== void 0 || flags.debug === true || flags.failOn !== void 0 || requiresStableConfiguredScope);
97291
+ };
97292
+ //#endregion
97293
+ //#region src/cli/commands/scan.ts
97294
+ const runScanCommand = async (input) => {
97295
+ const nodeMajorVersion = Number(process.versions.node.split(".")[0]);
97296
+ const tuiEnvironment = {
97297
+ flags: input.flags,
97298
+ isNonInteractiveEnvironment: isNonInteractiveEnvironment(),
97299
+ nodeMajorVersion,
97300
+ stdinIsTty: process.stdin.isTTY === true,
97301
+ stdoutIsTty: process.stdout.isTTY === true,
97302
+ supportsRawMode: typeof process.stdin.setRawMode === "function",
97303
+ terminalName: process.env.TERM
97304
+ };
97305
+ if (!shouldUseTui(tuiEnvironment)) {
97306
+ await inspectAction(input.directory, input.flags, input.invocationCommand);
97307
+ return;
97308
+ }
97309
+ await runProjectMigrations(path.resolve(input.directory));
97310
+ const scanTarget = await resolveScanTarget(input.directory, { allowAmbiguous: true });
97311
+ if (!shouldUseTui({
97312
+ ...tuiEnvironment,
97313
+ userConfig: scanTarget.userConfig
97314
+ })) {
97315
+ await inspectAction(input.directory, input.flags, input.invocationCommand);
97316
+ return;
97317
+ }
97318
+ recordCount(METRIC.cliInvoked, 1, { command: input.invocationCommand });
97319
+ const { runScanApp } = await import("./run-scan-app-DtYeufgr.js");
97320
+ const { shouldFail } = await runScanApp({
97321
+ directory: input.directory,
97322
+ scanTarget,
97323
+ options: resolveCliInspectOptions(input.flags, null),
97324
+ projectFlag: input.flags.project,
97325
+ skipPrompts: input.flags.yes ?? false,
97326
+ blocking: input.flags.blocking
97327
+ });
97328
+ if (shouldFail) process.exitCode = 1;
97329
+ };
97330
+ //#endregion
97264
97331
  //#region src/cli/utils/rule-catalog.ts
97265
97332
  const buildRuleCatalog = () => REACT_DOCTOR_RULES.map((entry) => ({
97266
97333
  key: entry.key,
@@ -97902,12 +97969,22 @@ const ensureWindowsUtf8Console = () => {
97902
97969
  };
97903
97970
  //#endregion
97904
97971
  //#region src/cli/utils/exit-gracefully.ts
97972
+ let didStartExiting = false;
97905
97973
  const exitGracefully = () => {
97974
+ if (didStartExiting) return process.exit(130);
97975
+ didStartExiting = true;
97906
97976
  try {
97907
97977
  if (isJsonModeActive()) writeJsonErrorReport(/* @__PURE__ */ new Error("Scan cancelled by user (SIGINT/SIGTERM)"));
97908
- else console.log("\nCancelled.\n");
97978
+ else console.log([
97979
+ "",
97980
+ "Cancelled.",
97981
+ "",
97982
+ buildSectionDivider(),
97983
+ "",
97984
+ ...buildFooterLinkLines({ shareUrl: null })
97985
+ ].join("\n"));
97909
97986
  } catch {}
97910
- process.exit(130);
97987
+ flushSentry().finally(() => process.exit(130));
97911
97988
  };
97912
97989
  //#endregion
97913
97990
  //#region src/cli/utils/guard-stdin.ts
@@ -98316,7 +98393,11 @@ ${highlighter.dim("Learn more:")}
98316
98393
  `;
98317
98394
  const collectCategoryOption = (value, previousValues) => [...previousValues ?? [], value];
98318
98395
  const program = new Command().name("react-doctor").description("Diagnose React codebase health").version(VERSION, "-v, --version", "display the version number").argument("[directory]", "project directory to scan", ".").option("--lint", "enable linting").option("--no-lint", "skip linting").option("--dead-code", "enable dead-code analysis (default)").option("--no-dead-code", "skip dead-code analysis (unused files / exports / dependencies, circular imports)").option("--supply-chain", "enable the dependency supply-chain scan (default)").option("--no-supply-chain", "skip the dependency supply-chain scan (Socket.dev dependency health checks)").option("--verbose", "show every rule and per-file details (default shows top 3 rules)").option("--debug", "force a Sentry trace and print its id at the end (paste it into a bug report)").option("--output-dir <dir>", "directory for the full diagnostics dump (default: a temp folder)").option("--score", "output only the score").option("--json", "output a single structured JSON report (suppresses other output)").option("--json-compact", "with --json, emit compact JSON (no indentation)").option("--json-out <path>", "with --json, write the report to a file instead of stdout").option("-y, --yes", "skip prompts, scan all workspace projects").option("--no-parallel", "lint serially with one worker (default: parallel across CPU cores; set the worker count with REACT_DOCTOR_PARALLEL)").option("--project <name>", "select projects: workspace names or directory paths (comma-separated for multiple); overrides the `projects` config field").option("--scope <value>", "how much supported JS/TS and inline HTML script source to scan/report: full (default), files, changed (only new issues vs base), or lines (issues whose source spans touch changed lines)").option("--base <ref>", "base git ref for files/changed/lines scope (auto-detected when omitted)").option("--include-untracked", "with --scope files/changed/lines, also scan ordinary untracked files (respects .gitignore)").addOption(new Option("--diff [base]", "[deprecated] alias for --scope changed (pass `false` to force a full scan)").hideHelp()).addOption(new Option("--changed-files-from <file>", "scan source files listed in a newline-delimited changed-files file").hideHelp()).option("--no-score", "skip the score API, the share URL, and crash reporting").addOption(new Option("--category <category>", "only show diagnostics in a category (repeatable; e.g. Security)").argParser(collectCategoryOption)).option("--no-telemetry", "alias for --no-score (skip the score API, share URL, and crash reporting)").option("--staged", "scan only staged (git index) files for pre-commit hooks (honors --project and config `projects`; exits 0 when nothing is staged)").option("--max-duration <seconds>", "scan time budget for the whole run, shared across workspace projects: past it, remaining lint batches and dead-code are skipped and partial results are reported (skipped files are listed in the JSON report)").option("--blocking <level>", "severity that fails CI: error (default), warning, or none (advisory)").addOption(new Option("--fail-on <level>", "[deprecated] alias for --blocking <level>").hideHelp()).option("--no-respect-inline-disables", "audit mode: neutralize inline lint suppressions before scanning").option("--warnings", "show warning-severity diagnostics (default)").option("--no-warnings", "hide warning-severity diagnostics (errors only)").option("--color", "force colored output").option("--no-color", "disable colored output (also honors NO_COLOR)").addHelpText("after", renderRootHelpEpilog);
98319
- program.action(inspectAction);
98396
+ program.action((directory = ".", flags) => runScanCommand({
98397
+ directory,
98398
+ flags,
98399
+ invocationCommand: "inspect"
98400
+ }));
98320
98401
  program.command("design [directory]").description("Run only the focused UI design diagnostics").addHelpText("after", renderDesignHelpEpilog).action((directory, _options, command) => designAction(directory ?? ".", command.optsWithGlobals()));
98321
98402
  program.command("why <location>").description("Explain why a rule fired (or why a suppression didn't apply) at a file:line").option("--project <name>", "select projects: workspace names or directory paths (comma-separated for multiple)").option("-c, --cwd <cwd>", "working directory", process.cwd()).option("--color", "force colored output").option("--no-color", "disable colored output (also honors NO_COLOR)").action((location, options) => whyAction(location, options));
98322
98403
  program.command("install").alias("setup").description("Install the react-doctor skill into your coding agents and optional git hook").option("-y, --yes", "skip prompts, install for all detected agents").option("--dry-run", "show what would be installed without writing files").option("--agent-hooks", "install native non-blocking agent hooks for Claude Code and Cursor").option("-c, --cwd <cwd>", "working directory", process.cwd()).option("--color", "force colored output").option("--no-color", "disable colored output (also honors NO_COLOR)").addHelpText("after", renderInstallHelpEpilog).action(installAction);
@@ -98337,38 +98418,11 @@ rules.command("category <category> <severity>").description("Set the severity fo
98337
98418
  rules.command("ignore-tag <tag>").description("Skip a whole rule family by tag before linting (e.g. design)").option("-c, --cwd <cwd>", "working directory", process.cwd()).action((tag, _options, command) => rulesIgnoreTagAction(tag, command.optsWithGlobals()));
98338
98419
  rules.command("unignore-tag <tag>").description("Stop ignoring a tag previously skipped via ignore-tag").option("-c, --cwd <cwd>", "working directory", process.cwd()).action((tag, _options, command) => rulesUnignoreTagAction(tag, command.optsWithGlobals()));
98339
98420
  program.command("experimental-lsp", { hidden: false }).description("[experimental] run the React Doctor language server over stdio (for editors)").allowUnknownOption().action(() => {});
98340
- program.command("experimental-tui [directory]", { hidden: true }).description("[experimental] interactive, scrollable scan report").option("--blocking <level>", "severity that fails CI: error (default), warning, or none (advisory)").option("--color", "force colored output").option("--no-color", "disable colored output (also honors NO_COLOR)").option("--no-dead-code", "skip dead-code analysis").option("--no-supply-chain", "skip the dependency supply-chain scan").option("--no-score", "skip the score API, the share URL, and crash reporting").option("-p, --project <names>", "scan specific workspace projects (comma-separated, or *)").option("-y, --yes", "skip the project prompt and scan every discovered project").action(async (directory = ".", _localOptions, command) => {
98341
- const options = command.optsWithGlobals();
98342
- const deadCode = options.deadCode === false ? false : void 0;
98343
- const noScore = options.score === false ? true : void 0;
98344
- const supplyChain = options.supplyChain === false ? false : void 0;
98345
- const nodeMajorVersion = Number(process.versions.node.split(".")[0]);
98346
- if (process.stdout.isTTY !== true || process.stdin.isTTY !== true || isNonInteractiveEnvironment() || nodeMajorVersion < 22) {
98347
- await inspectAction(directory, {
98348
- deadCode,
98349
- score: options.score === false ? false : void 0,
98350
- supplyChain,
98351
- project: options.project,
98352
- yes: options.yes,
98353
- blocking: options.blocking
98354
- });
98355
- return;
98356
- }
98357
- recordCount(METRIC.cliInvoked, 1, { command: "experimental-tui" });
98358
- const { runScanApp } = await import("./run-scan-app-DHRvqfkX.js");
98359
- const { shouldFail } = await runScanApp({
98360
- directory,
98361
- options: {
98362
- deadCode,
98363
- noScore,
98364
- supplyChain
98365
- },
98366
- projectFlag: options.project,
98367
- skipPrompts: options.yes ?? false,
98368
- blocking: options.blocking
98369
- });
98370
- if (shouldFail) process.exitCode = 1;
98371
- });
98421
+ program.command("experimental-tui [directory]", { hidden: true }).description("[experimental] interactive, scrollable scan report").option("--blocking <level>", "severity that fails CI: error (default), warning, or none (advisory)").option("--color", "force colored output").option("--no-color", "disable colored output (also honors NO_COLOR)").option("--no-dead-code", "skip dead-code analysis").option("--no-supply-chain", "skip the dependency supply-chain scan").option("--score", "only print the numeric score (for scripts and CI)").option("--no-score", "skip the score API, the share URL, and crash reporting").option("-p, --project <names>", "scan specific workspace projects (comma-separated, or *)").option("-y, --yes", "skip the project prompt and scan every discovered project").action((directory = ".", _localOptions, command) => runScanCommand({
98422
+ directory,
98423
+ flags: command.optsWithGlobals(),
98424
+ invocationCommand: "experimental-tui"
98425
+ }));
98372
98426
  process.stdout.on("error", (error) => {
98373
98427
  if (error.code === "EPIPE") process.exit(0);
98374
98428
  });
@@ -98395,7 +98449,7 @@ Promise.resolve().then(() => assertNoRemovedFlags(process.argv)).then(() => prog
98395
98449
  handleError(error, { sentryEventId });
98396
98450
  });
98397
98451
  //#endregion
98398
- export { toForwardSlashes as A, getCategoryImpact as B, buildRulePriorityMap as C, resolveBlockingLevel as D, formatLearnMoreLine as E, TUI_REPORT_DETAIL_WIDTH_FRACTION as F, runPromise as G, mapWithConcurrency as H, isCiEnvironment as I, succeed$3 as J, sync as K, DIAGNOSTIC_CATEGORY_BUCKETS as L, isUnicodeSupported as M, METRIC as N, isReactDoctorWorkflowInstalled as O, TUI_REPORT_COMPACT_MAX_ROWS as P, Progress as R, hasLintHardFailure as S, formatFixRecipeLine as T, mergeReactDoctorConfigs as U, highlighter as V, resolveScanTarget as W, canAnimateOnboarding as _, resolveCliInspectOptions as a, buildCodeFrame as b, detectLaunchableAgents as c, launchCliAgent as d, isShareOptedOut as f, doctorFace as g, easeOutCubic as h, selectProjects as i, findNearestPackageDirectory as j, recordCount as k, CLI_AGENT_BINARIES as l, printFooter as m, shouldFailScanGate as n, countUniqueScannedFiles as o, createInvocationInspect as p, void_ as q, discoverWorkspacePackages as r, setUpGitHubActions as s, exitGracefully as t, copyToClipboard as u, computeProjectedScore as v, buildSortedRuleGroups as w, buildNoScoreMessage as x, formatElapsedTime as y, Reporter as z };
98452
+ export { highlighter as $, hasLintHardFailure as A, isUnicodeSupported as B, shouldRecordOnboarding as C, buildCodeFrame as D, formatElapsedTime as E, resolveBlockingLevel as F, isCiEnvironment as G, METRIC as H, isReactDoctorWorkflowInstalled as I, Progress as J, DIAGNOSTIC_CATEGORY_BUCKETS as K, recordCount as L, buildSortedRuleGroups as M, formatFixRecipeLine as N, buildEmptyReportMessage as O, formatLearnMoreLine as P, hasPublishedFixRecipe as Q, toForwardSlashes as R, isOnboardingForced as S, computeProjectedScore as T, TUI_REPORT_COMPACT_MAX_ROWS as U, CI_TRUST_COMPANIES as V, TUI_REPORT_DETAIL_WIDTH_FRACTION as W, buildRuleDocsUrl as X, Reporter as Y, getCategoryImpact as Z, printFooter as _, resolveCliInspectOptions as a, void_ as at, markOnboardingComplete as b, setUpGitHubActions as c, CLI_AGENT_BINARIES as d, mapWithConcurrency as et, copyToClipboard as f, createInvocationInspect as g, isShareOptedOut as h, selectProjects as i, sync as it, buildRulePriorityMap as j, buildNoScoreMessage as k, openUrl as l, buildHandoffPayload as m, shouldFailScanGate as n, resolveScanTarget as nt, filterScansForSurface as o, succeed$3 as ot, launchCliAgent as p, GITHUB_ACTIONS_SETUP_URL as q, discoverWorkspacePackages as r, runPromise as rt, countUniqueScannedFiles as s, exitGracefully as t, mergeReactDoctorConfigs as tt, detectLaunchableAgents as u, easeOutCubic as v, filterDiagnosticsByCategories as w, canAnimateOnboarding as x, doctorFace as y, findNearestPackageDirectory as z };
98399
98453
 
98400
98454
  //# sourceMappingURL=cli.js.map
98401
- //# debugId=1a34c548-5d3e-559c-8a3a-8a869ca2847a
98455
+ //# debugId=e4075e6f-4128-5af6-bc97-656272450282
package/dist/lsp.js CHANGED
@@ -76795,9 +76795,12 @@ const METRIC = {
76795
76795
  lspScanCompleted: "lsp.scan.completed",
76796
76796
  lspScanDuration: "lsp.scan.duration",
76797
76797
  lspScanDiagnostics: "lsp.scan.diagnostics",
76798
- tuiActionMenuOpened: "tui.action_menu_opened",
76799
76798
  tuiCompactReportShown: "tui.compact_report_shown",
76800
76799
  tuiFindingNavigated: "tui.finding_navigated",
76800
+ tuiIssueStreamShown: "tui.issue_stream_shown",
76801
+ tuiProjectPathContextShown: "tui.project_path_context_shown",
76802
+ tuiReportActionSelected: "tui.report_action_selected",
76803
+ tuiCancelled: "tui.cancelled",
76801
76804
  tuiStackedReportCapped: "tui.stacked_report_capped",
76802
76805
  aiTrainingWarningShown: "ai.training.warning_shown",
76803
76806
  jsonOutUsed: "json.out_used"
@@ -77100,5 +77103,5 @@ const startLanguageServer = () => {
77100
77103
  };
77101
77104
  //#endregion
77102
77105
  export { startLanguageServer };
77103
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="a07beba2-2d2e-59f0-8edb-430554d791d1")}catch(e){}}();
77104
- //# debugId=a07beba2-2d2e-59f0-8edb-430554d791d1
77106
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="84561d6a-3814-52df-a86e-cf40018cdc87")}catch(e){}}();
77107
+ //# debugId=84561d6a-3814-52df-a86e-cf40018cdc87