react-doctor 0.8.3-dev.416fdcc → 0.8.3-dev.76263ec

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]="be7f877d-9463-5212-8e1f-dc5feea97da0")}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]="81e6305d-aa88-52f6-82ff-ac652d90107a")}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";
@@ -64964,14 +64964,6 @@ const DIAGNOSTIC_CATEGORY_BUCKETS = [
64964
64964
  "Maintainability"
64965
64965
  ];
64966
64966
  const OCCURRENCE_MATCHED_CATEGORIES = new Set(["Accessibility"]);
64967
- const CATEGORY_IMPACT_BY_NAME = new Map(Object.entries({
64968
- Security: "An attacker can read user data, act as your users, or run code you never shipped.",
64969
- Bugs: "Real users hit crashes, wrong output, or state that silently corrupts.",
64970
- Performance: "Users feel extra latency and wasted renders on every interaction.",
64971
- Accessibility: "Users on screen readers, keyboards, or assistive tech get locked out.",
64972
- Maintainability: "Every future change gets slower and riskier to make."
64973
- }));
64974
- const getCategoryImpact = (category) => CATEGORY_IMPACT_BY_NAME.get(category);
64975
64967
  const APP_ONLY_RULE_KEYS = new Set([
64976
64968
  "react-hooks-js/static-components",
64977
64969
  "react-doctor/no-render-prop-children",
@@ -77317,7 +77309,6 @@ const BASELINE_FILES_TEMP_DIR_PREFIX = "react-doctor-baseline-";
77317
77309
  const SCAN_RESULT_CACHE_FILENAME = "scan-cache.json";
77318
77310
  const RUN_GIT_MAX_BUFFER_BYTES = 64 * 1024 * 1024;
77319
77311
  const GH_DEFAULT_BRANCH_PROBE_TIMEOUT_MS = 5e3;
77320
- const TUI_REPORT_DETAIL_WIDTH_FRACTION = .6;
77321
77312
  const CI_TRUST_COMPANIES = "PayPal, Rippling, and Alibaba";
77322
77313
  const WELCOME_EXPLANATION_HOLD_MS = 1e3;
77323
77314
  const CATEGORY_COUNTUP_SETTLE_HOLD_MS = 1e3;
@@ -77409,7 +77400,7 @@ const makeNoopConsole = () => ({
77409
77400
  });
77410
77401
  //#endregion
77411
77402
  //#region src/cli/utils/version.ts
77412
- const VERSION = "0.8.3-dev.416fdcc";
77403
+ const VERSION = "0.8.3-dev.76263ec";
77413
77404
  //#endregion
77414
77405
  //#region src/cli/utils/json-mode.ts
77415
77406
  let context = null;
@@ -77767,13 +77758,13 @@ const isDevVersion = (version) => version === "0.0.0" || version.includes("-");
77767
77758
  * uploads source-map artifacts under, so stack frames symbolicate. Honors the
77768
77759
  * standard `SENTRY_RELEASE` override.
77769
77760
  */
77770
- const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.8.3-dev.416fdcc`;
77761
+ const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.8.3-dev.76263ec`;
77771
77762
  /**
77772
77763
  * Deployment environment shown in Sentry's environment filter. Defaults to
77773
77764
  * `production` for tagged releases and `development` for dev/unbuilt versions,
77774
77765
  * overridable via the standard `SENTRY_ENVIRONMENT` env var.
77775
77766
  */
77776
- const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.8.3-dev.416fdcc") ? "development" : "production");
77767
+ const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.8.3-dev.76263ec") ? "development" : "production");
77777
77768
  /**
77778
77769
  * Performance-tracing sample rate in `[0, 1]`. Reads `SENTRY_TRACES_SAMPLE_RATE`
77779
77770
  * (set to `0` to disable tracing) and falls back to
@@ -89620,13 +89611,12 @@ const buildRuntimeLayers = (input) => {
89620
89611
  const scoreLayer = input.shouldComputeScore ? Score.layerHttp : Score.layerOf(null);
89621
89612
  const supplyChainLayer = input.shouldRunSupplyChain ? SupplyChain.layerNode : SupplyChain.layerOf([]);
89622
89613
  const projectLayer = input.projectInfoOverride === void 0 ? Project.layerNode : Project.layerOf(input.projectInfoOverride);
89623
- const progressLayer = input.progressLayer ?? (input.shouldShowProgressSpinners ? Progress.layerOra(buildSpinnerProgressHandle) : Progress.layerNoop);
89624
- const reporterLayer = input.reporterLayer ?? Reporter.layerNoop;
89614
+ const progressLayer = input.shouldShowProgressSpinners ? Progress.layerOra(buildSpinnerProgressHandle) : Progress.layerNoop;
89625
89615
  const baseLayers = mergeAll$1(projectLayer, input.hasConfigOverride ? Config.layerOf({
89626
89616
  config: input.userConfig,
89627
89617
  resolvedDirectory: input.directory,
89628
89618
  configSourceDirectory: input.configSourceDirectory
89629
- }) : Config.layerNode, Files.layerNode, Git.layerNode, linterLayer, LintPartialFailures.layerLive, deadCodeLayer, progressLayer, reporterLayer, scoreLayer, supplyChainLayer);
89619
+ }) : Config.layerNode, Files.layerNode, Git.layerNode, linterLayer, LintPartialFailures.layerLive, deadCodeLayer, progressLayer, Reporter.layerNoop, scoreLayer, supplyChainLayer);
89630
89620
  return input.oxlintConcurrency === void 0 ? baseLayers : mergeAll$1(baseLayers, succeed$3(OxlintConcurrency, input.oxlintConcurrency));
89631
89621
  };
89632
89622
  //#endregion
@@ -91461,9 +91451,6 @@ const supportsHyperlinks = (stream = process.stdout, env = process.env) => {
91461
91451
  * would choke on the escape sequences).
91462
91452
  */
91463
91453
  const shouldRenderHyperlinks = (stream = process.stdout) => supportsHyperlinks(stream) && !isCodingAgentEnvironment();
91464
- //#endregion
91465
- //#region src/cli/utils/should-show-share-link.ts
91466
- const shouldShowShareLink = ({ noScore, share, isCi }) => !noScore && share && !isCi;
91467
91454
  const FORCE_ONBOARDING_ENV_VAR = "REACT_DOCTOR_FORCE_ONBOARDING";
91468
91455
  const FALSY_FLAG_VALUES = new Set([
91469
91456
  "",
@@ -91682,13 +91669,6 @@ const colorizeByScore = (text, score) => {
91682
91669
  return highlighter.error(text);
91683
91670
  };
91684
91671
  //#endregion
91685
- //#region src/cli/utils/doctor-face.ts
91686
- const doctorFace = (score) => {
91687
- if (score >= 75) return ["◠ ◠", " ▽ "];
91688
- if (score >= 50) return ["• •", " ─ "];
91689
- return ["x x", " ▽ "];
91690
- };
91691
- //#endregion
91692
91672
  //#region src/cli/utils/ease-out-cubic.ts
91693
91673
  const easeOutCubic = (progress) => 1 - (1 - progress) ** 3;
91694
91674
  //#endregion
@@ -91754,10 +91734,15 @@ const buildProjectedScoreBar = (currentScore, potentialScore) => {
91754
91734
  const emptyCount = Math.max(0, barWidth - currentFill - gainCount);
91755
91735
  return colorizeByScore("█".repeat(currentFill), currentScore) + highlighter.dim(colorizeByScore("▓".repeat(gainCount), currentScore)) + highlighter.dim("░".repeat(emptyCount));
91756
91736
  };
91737
+ const getDoctorFace = (score) => {
91738
+ if (score >= 75) return ["◠ ◠", " ▽ "];
91739
+ if (score >= 50) return ["• •", " ─ "];
91740
+ return ["x x", " ▽ "];
91741
+ };
91757
91742
  const BRANDING_LINE = `React Doctor ${highlighter.dim("(https://react.doctor)")}`;
91758
91743
  const RAW_BRANDING_LINE = "React Doctor (https://react.doctor)";
91759
91744
  const buildRawFaceLines = (score) => {
91760
- const [eyes, mouth] = doctorFace(score);
91745
+ const [eyes, mouth] = getDoctorFace(score);
91761
91746
  return [
91762
91747
  FACE_BOX_TOP_BORDER,
91763
91748
  `│ ${eyes} │`,
@@ -92091,8 +92076,7 @@ const mergeInspectOptions = (inputOptions, userConfig) => {
92091
92076
  includeTagDefaults: inputOptions.includeTagDefaults ?? false,
92092
92077
  scoreDisabledMessage: inputOptions.scoreDisabledMessage,
92093
92078
  outputSurface: inputOptions.outputSurface ?? "cli",
92094
- suppressRendering: (inputOptions.suppressRendering ?? false) || inputOptions.uiLayers != null,
92095
- uiLayers: inputOptions.uiLayers ?? null,
92079
+ suppressRendering: inputOptions.suppressRendering ?? false,
92096
92080
  concurrentScan: inputOptions.concurrentScan ?? false,
92097
92081
  concurrency: inputOptions.concurrency,
92098
92082
  maxDurationMs: inputOptions.maxDurationMs ?? null,
@@ -92316,9 +92300,7 @@ const runInspectWithRuntime = async (directory, options, userConfig, hasConfigOv
92316
92300
  shouldRunSupplyChain: options.supplyChain,
92317
92301
  shouldComputeScore: !options.noScore,
92318
92302
  shouldShowProgressSpinners,
92319
- oxlintConcurrency: options.concurrency,
92320
- reporterLayer: options.uiLayers?.reporter,
92321
- progressLayer: options.uiLayers?.progress
92303
+ oxlintConcurrency: options.concurrency
92322
92304
  });
92323
92305
  const program = runInspect({
92324
92306
  directory,
@@ -92356,7 +92338,7 @@ const runInspectWithRuntime = async (directory, options, userConfig, hasConfigOv
92356
92338
  const output = await runPromise(restoreLegacyThrow(applyObservability(options.silent ? program.pipe(provide(layers), provideService$2(Console, silentConsole)) : program.pipe(provide(layers)), rootSentrySpan)));
92357
92339
  const didLintFail = lintBindingMissing || output.didLintFail;
92358
92340
  const lintFailureReason = lintBindingMissing ? `oxlint native binding not found for Node ${process.version}; expected one matching ${OXLINT_NODE_REQUIREMENT}` : output.lintFailureReason;
92359
- if (!options.scoreOnly && !options.uiLayers && !lintBindingMissing && output.didLintFail && lintFailureReason !== null) if (output.lintFailureReasonKind === "native-binding-missing") runConsole(log(highlighter.gray(` Upgrade to Node ${OXLINT_NODE_REQUIREMENT} or run: npx -p oxlint@latest react-doctor@latest`)));
92341
+ if (!options.scoreOnly && !lintBindingMissing && output.didLintFail && lintFailureReason !== null) if (output.lintFailureReasonKind === "native-binding-missing") runConsole(log(highlighter.gray(` Upgrade to Node ${OXLINT_NODE_REQUIREMENT} or run: npx -p oxlint@latest react-doctor@latest`)));
92360
92342
  else runConsole(error$1(highlighter.error(lintFailureReason)));
92361
92343
  let inspectDiagnostics = output.diagnostics;
92362
92344
  let baselineDelta;
@@ -92591,7 +92573,7 @@ const finalizeAndRender = (input) => gen(function* () {
92591
92573
  const scoreDiagnostics = filterDiagnosticsForSurface([...diagnostics], "score", userConfig);
92592
92574
  const displayedScoreDiagnostics = filterDiagnosticsForSurface([...printedDiagnostics], "score", userConfig);
92593
92575
  const potentialScore = score ? yield* promise(() => computeProjectedScore(displayedScoreDiagnostics, scoreDiagnostics, score)) : null;
92594
- const showShareLink = shouldShowShareLink(options);
92576
+ const shouldShowShareLink = !options.noScore && options.share && !options.isCi;
92595
92577
  yield* pause;
92596
92578
  yield* printSummary({
92597
92579
  diagnostics: [...printedDiagnostics],
@@ -92614,7 +92596,7 @@ const finalizeAndRender = (input) => gen(function* () {
92614
92596
  diagnostics: [...printedDiagnostics],
92615
92597
  scoreResult: score,
92616
92598
  projectName: project.projectName,
92617
- isOffline: !showShareLink
92599
+ isOffline: !shouldShowShareLink
92618
92600
  });
92619
92601
  return buildResult();
92620
92602
  });
@@ -92724,127 +92706,6 @@ const computeDefaultSelectedAgents = (detectedAgents, rememberedAgents) => {
92724
92706
  return detectedAgents.length === 1 ? [...detectedAgents] : [];
92725
92707
  };
92726
92708
  //#endregion
92727
- //#region src/cli/utils/launch-agent.ts
92728
- const isWindows = process.platform === "win32";
92729
- const CLI_AGENT_IDS = [
92730
- "claude-code",
92731
- "codex",
92732
- "cursor"
92733
- ];
92734
- const CLI_AGENT_BINARIES = {
92735
- "claude-code": "claude",
92736
- codex: "codex",
92737
- cursor: "cursor-agent"
92738
- };
92739
- const CLI_AGENT_AUTO_FLAGS = {
92740
- "claude-code": ["--dangerously-skip-permissions"],
92741
- codex: ["--yolo"],
92742
- cursor: ["--force"]
92743
- };
92744
- const resolveCursorBundledNode = () => {
92745
- if (!isWindows) return null;
92746
- const localAppData = process.env.LOCALAPPDATA;
92747
- if (!localAppData) return null;
92748
- const cursorAgentRoot = Path.join(localAppData, "cursor-agent", "versions");
92749
- try {
92750
- const versions = NFS.readdirSync(cursorAgentRoot);
92751
- if (versions.length === 0) return null;
92752
- const sortedVersions = versions.sort((a, b) => b.localeCompare(a, void 0, { numeric: true }));
92753
- for (const version of sortedVersions) try {
92754
- const bundledNodePath = Path.join(cursorAgentRoot, version, "node.exe");
92755
- const entryScriptPath = Path.join(cursorAgentRoot, version, "index.js");
92756
- if (NFS.statSync(bundledNodePath).isFile() && NFS.statSync(entryScriptPath).isFile()) return {
92757
- command: bundledNodePath,
92758
- args: [entryScriptPath]
92759
- };
92760
- } catch {}
92761
- } catch {}
92762
- return null;
92763
- };
92764
- const resolveWindowsCmdEntryScript = (command) => {
92765
- if (!isWindows) return null;
92766
- const pathDirectories = (process.env.PATH ?? "").split(Path.delimiter).filter(Boolean);
92767
- for (const directory of pathDirectories) {
92768
- const cmdFilePath = Path.join(directory, `${command}.cmd`);
92769
- try {
92770
- if (!NFS.statSync(cmdFilePath).isFile()) continue;
92771
- } catch {
92772
- continue;
92773
- }
92774
- try {
92775
- const entryScriptMatch = NFS.readFileSync(cmdFilePath, "utf8").match(/"%(?:~dp0|dp0%)\\([^"]+\.(?:m?js|cjs))"/);
92776
- if (!entryScriptMatch) continue;
92777
- const resolvedScriptPath = Path.resolve(directory, entryScriptMatch[1]);
92778
- if (NFS.statSync(resolvedScriptPath).isFile()) return resolvedScriptPath;
92779
- } catch {}
92780
- }
92781
- return null;
92782
- };
92783
- const spawnAgent = (command, args, cwd) => new Promise((resolve, reject) => {
92784
- const child = spawn(command, [...args], {
92785
- cwd,
92786
- stdio: "inherit"
92787
- });
92788
- child.on("error", reject);
92789
- child.on("close", (code) => resolve(code ?? 0));
92790
- });
92791
- const launchCliAgent = async (agentId, prompt, cwd) => {
92792
- const binary = CLI_AGENT_BINARIES[agentId];
92793
- const agentArgs = [...CLI_AGENT_AUTO_FLAGS[agentId], prompt];
92794
- if (isWindows) {
92795
- if (agentId === "cursor") {
92796
- const cursorResolution = resolveCursorBundledNode();
92797
- if (cursorResolution) return spawnAgent(cursorResolution.command, [...cursorResolution.args, ...agentArgs], cwd);
92798
- }
92799
- const entryScript = resolveWindowsCmdEntryScript(binary);
92800
- if (entryScript) return spawnAgent(process.execPath, [entryScript, ...agentArgs], cwd);
92801
- }
92802
- try {
92803
- return await spawnAgent(binary, agentArgs, cwd);
92804
- } catch {
92805
- throw new Error(`Failed to launch ${binary}`);
92806
- }
92807
- };
92808
- const CLIPBOARD_COMMANDS = [
92809
- {
92810
- binary: "pbcopy",
92811
- args: []
92812
- },
92813
- {
92814
- binary: "wl-copy",
92815
- args: []
92816
- },
92817
- {
92818
- binary: "xclip",
92819
- args: ["-selection", "clipboard"]
92820
- },
92821
- {
92822
- binary: "xsel",
92823
- args: ["--clipboard", "--input"]
92824
- },
92825
- {
92826
- binary: "clip",
92827
- args: []
92828
- }
92829
- ];
92830
- const copyToClipboard = (text) => {
92831
- const command = CLIPBOARD_COMMANDS.find((candidate) => isCommandAvailable(candidate.binary));
92832
- if (!command) return Promise.resolve(false);
92833
- return new Promise((resolve) => {
92834
- const child = spawn(command.binary, command.args);
92835
- child.on("error", () => resolve(false));
92836
- child.on("close", (code) => resolve(code === 0));
92837
- child.stdin.on("error", () => resolve(false));
92838
- child.stdin.end(text);
92839
- });
92840
- };
92841
- //#endregion
92842
- //#region src/cli/utils/detect-launchable-agents.ts
92843
- const detectLaunchableAgents = async () => {
92844
- const detected = new Set(await detectAvailableAgents());
92845
- return CLI_AGENT_IDS.filter((agentId) => detected.has(agentId) && isCommandAvailable(CLI_AGENT_BINARIES[agentId]));
92846
- };
92847
- //#endregion
92848
92709
  //#region src/cli/utils/action-upgrade-prompt.ts
92849
92710
  const ACTION_UPGRADE_GATE = {
92850
92711
  id: ACTION_UPGRADE_EVENT,
@@ -94087,6 +93948,116 @@ const installReactDoctorSkillForAgent = async (agent, projectRoot) => {
94087
93948
  return result.skills.length > 0 && result.failed.length === 0;
94088
93949
  };
94089
93950
  //#endregion
93951
+ //#region src/cli/utils/launch-agent.ts
93952
+ const isWindows = process.platform === "win32";
93953
+ const CLI_AGENT_BINARIES = {
93954
+ "claude-code": "claude",
93955
+ codex: "codex",
93956
+ cursor: "cursor-agent"
93957
+ };
93958
+ const CLI_AGENT_AUTO_FLAGS = {
93959
+ "claude-code": ["--dangerously-skip-permissions"],
93960
+ codex: ["--yolo"],
93961
+ cursor: ["--force"]
93962
+ };
93963
+ const resolveCursorBundledNode = () => {
93964
+ if (!isWindows) return null;
93965
+ const localAppData = process.env.LOCALAPPDATA;
93966
+ if (!localAppData) return null;
93967
+ const cursorAgentRoot = Path.join(localAppData, "cursor-agent", "versions");
93968
+ try {
93969
+ const versions = NFS.readdirSync(cursorAgentRoot);
93970
+ if (versions.length === 0) return null;
93971
+ const sortedVersions = versions.sort((a, b) => b.localeCompare(a, void 0, { numeric: true }));
93972
+ for (const version of sortedVersions) try {
93973
+ const bundledNodePath = Path.join(cursorAgentRoot, version, "node.exe");
93974
+ const entryScriptPath = Path.join(cursorAgentRoot, version, "index.js");
93975
+ if (NFS.statSync(bundledNodePath).isFile() && NFS.statSync(entryScriptPath).isFile()) return {
93976
+ command: bundledNodePath,
93977
+ args: [entryScriptPath]
93978
+ };
93979
+ } catch {}
93980
+ } catch {}
93981
+ return null;
93982
+ };
93983
+ const resolveWindowsCmdEntryScript = (command) => {
93984
+ if (!isWindows) return null;
93985
+ const pathDirectories = (process.env.PATH ?? "").split(Path.delimiter).filter(Boolean);
93986
+ for (const directory of pathDirectories) {
93987
+ const cmdFilePath = Path.join(directory, `${command}.cmd`);
93988
+ try {
93989
+ if (!NFS.statSync(cmdFilePath).isFile()) continue;
93990
+ } catch {
93991
+ continue;
93992
+ }
93993
+ try {
93994
+ const entryScriptMatch = NFS.readFileSync(cmdFilePath, "utf8").match(/"%(?:~dp0|dp0%)\\([^"]+\.(?:m?js|cjs))"/);
93995
+ if (!entryScriptMatch) continue;
93996
+ const resolvedScriptPath = Path.resolve(directory, entryScriptMatch[1]);
93997
+ if (NFS.statSync(resolvedScriptPath).isFile()) return resolvedScriptPath;
93998
+ } catch {}
93999
+ }
94000
+ return null;
94001
+ };
94002
+ const spawnAgent = (command, args, cwd) => new Promise((resolve, reject) => {
94003
+ const child = spawn(command, [...args], {
94004
+ cwd,
94005
+ stdio: "inherit"
94006
+ });
94007
+ child.on("error", reject);
94008
+ child.on("close", (code) => resolve(code ?? 0));
94009
+ });
94010
+ const launchCliAgent = async (agentId, prompt, cwd) => {
94011
+ const binary = CLI_AGENT_BINARIES[agentId];
94012
+ const agentArgs = [...CLI_AGENT_AUTO_FLAGS[agentId], prompt];
94013
+ if (isWindows) {
94014
+ if (agentId === "cursor") {
94015
+ const cursorResolution = resolveCursorBundledNode();
94016
+ if (cursorResolution) return spawnAgent(cursorResolution.command, [...cursorResolution.args, ...agentArgs], cwd);
94017
+ }
94018
+ const entryScript = resolveWindowsCmdEntryScript(binary);
94019
+ if (entryScript) return spawnAgent(process.execPath, [entryScript, ...agentArgs], cwd);
94020
+ }
94021
+ try {
94022
+ return await spawnAgent(binary, agentArgs, cwd);
94023
+ } catch {
94024
+ throw new Error(`Failed to launch ${binary}`);
94025
+ }
94026
+ };
94027
+ const CLIPBOARD_COMMANDS = [
94028
+ {
94029
+ binary: "pbcopy",
94030
+ args: []
94031
+ },
94032
+ {
94033
+ binary: "wl-copy",
94034
+ args: []
94035
+ },
94036
+ {
94037
+ binary: "xclip",
94038
+ args: ["-selection", "clipboard"]
94039
+ },
94040
+ {
94041
+ binary: "xsel",
94042
+ args: ["--clipboard", "--input"]
94043
+ },
94044
+ {
94045
+ binary: "clip",
94046
+ args: []
94047
+ }
94048
+ ];
94049
+ const copyToClipboard = (text) => {
94050
+ const command = CLIPBOARD_COMMANDS.find((candidate) => isCommandAvailable(candidate.binary));
94051
+ if (!command) return Promise.resolve(false);
94052
+ return new Promise((resolve) => {
94053
+ const child = spawn(command.binary, command.args);
94054
+ child.on("error", () => resolve(false));
94055
+ child.on("close", (code) => resolve(code === 0));
94056
+ child.stdin.on("error", () => resolve(false));
94057
+ child.stdin.end(text);
94058
+ });
94059
+ };
94060
+ //#endregion
94090
94061
  //#region src/cli/utils/handoff-to-agent.ts
94091
94062
  const CLIPBOARD_CHOICE = "clipboard";
94092
94063
  const SKIP_CHOICE = "skip";
@@ -94150,6 +94121,10 @@ const maybeOfferActionUpgrade = async (projectRoot) => {
94150
94121
  }
94151
94122
  if (await upgradeGitHubActionsWorkflow(workflow)) recordActionUpgradeDecision(projectRoot, "accepted");
94152
94123
  };
94124
+ const detectLaunchableAgents = async () => {
94125
+ const detected = new Set(await detectAvailableAgents());
94126
+ return Object.keys(CLI_AGENT_BINARIES).filter((agentId) => detected.has(agentId) && isCommandAvailable(CLI_AGENT_BINARIES[agentId]));
94127
+ };
94153
94128
  const handoffToAgent = async (input) => {
94154
94129
  if (!input.interactive || input.diagnostics.length === 0) return;
94155
94130
  cliLogger.break();
@@ -94464,29 +94439,15 @@ const readChangedFilesFrom = (filePath) => {
94464
94439
  return [...uniqueFiles];
94465
94440
  };
94466
94441
  //#endregion
94467
- //#region src/cli/utils/count-unique-scanned-files.ts
94468
- const countUniqueScannedFiles = (results) => {
94469
- const uniqueScannedFilePaths = /* @__PURE__ */ new Set();
94470
- let fileCountFromScansWithoutPaths = 0;
94471
- for (const result of results) {
94472
- const scannedFilePaths = result.scannedFilePaths;
94473
- if (scannedFilePaths && scannedFilePaths.length > 0) for (const filePath of scannedFilePaths) uniqueScannedFilePaths.add(filePath);
94474
- else fileCountFromScansWithoutPaths += result.scannedFileCount ?? result.project.sourceFileCount;
94475
- }
94476
- return uniqueScannedFilePaths.size + fileCountFromScansWithoutPaths;
94477
- };
94442
+ //#region src/cli/utils/filter-scans-for-surface.ts
94443
+ const filterScansForSurface = (completedScans, surface) => completedScans.flatMap((scan) => filterDiagnosticsForSurface([...scan.result.diagnostics], surface, scan.config));
94478
94444
  //#endregion
94479
- //#region src/cli/utils/score-band-label.ts
94480
- const scoreBandLabel = (score) => {
94445
+ //#region src/cli/utils/render-multi-project-summary.ts
94446
+ const getScoreLabel = (score) => {
94481
94447
  if (score >= 75) return "Great";
94482
94448
  if (score >= 50) return "OK";
94483
94449
  return "Needs work";
94484
94450
  };
94485
- //#endregion
94486
- //#region src/cli/utils/filter-scans-for-surface.ts
94487
- const filterScansForSurface = (completedScans, surface) => completedScans.flatMap((scan) => filterDiagnosticsForSurface([...scan.result.diagnostics], surface, scan.config));
94488
- //#endregion
94489
- //#region src/cli/utils/render-multi-project-summary.ts
94490
94451
  const buildSummaryLine = (entry, longestProjectNameLength) => {
94491
94452
  const paddedName = entry.projectName.padEnd(longestProjectNameLength);
94492
94453
  const nameRendering = entry.score !== null ? colorizeByScore(paddedName, entry.score) : highlighter.dim(paddedName);
@@ -94495,7 +94456,7 @@ const buildSummaryLine = (entry, longestProjectNameLength) => {
94495
94456
  return ` ${nameRendering} ${highlighter.dim("no score")} ${highlighter.dim(issueLabel)}`;
94496
94457
  }
94497
94458
  const scoreRendering = colorizeByScore(String(entry.score).padStart(3), entry.score);
94498
- const label = colorizeByScore(scoreBandLabel(entry.score), entry.score);
94459
+ const label = colorizeByScore(getScoreLabel(entry.score), entry.score);
94499
94460
  const issuesParts = [];
94500
94461
  if (entry.errorCount > 0) issuesParts.push(highlighter.error(`${entry.errorCount} ${entry.errorCount === 1 ? "error" : "errors"}`));
94501
94462
  const warningCount = entry.issueCount - entry.errorCount;
@@ -94517,7 +94478,14 @@ const printMultiProjectSummary = (input) => gen(function* () {
94517
94478
  const projectRootByDiagnostic = /* @__PURE__ */ new WeakMap();
94518
94479
  for (const scan of completedScans) for (const diagnostic of scan.result.diagnostics) projectRootByDiagnostic.set(diagnostic, scan.result.project.rootDirectory);
94519
94480
  const resolveDiagnosticSourceRoot = (diagnostic) => projectRootByDiagnostic.get(diagnostic) ?? "";
94520
- const totalScannedFileCount = countUniqueScannedFiles(completedScans.map((scan) => scan.result));
94481
+ const uniqueScannedFilePaths = /* @__PURE__ */ new Set();
94482
+ let fileCountFromScansWithoutPaths = 0;
94483
+ for (const scan of completedScans) {
94484
+ const scannedFilePaths = scan.result.scannedFilePaths;
94485
+ if (scannedFilePaths && scannedFilePaths.length > 0) for (const filePath of scannedFilePaths) uniqueScannedFilePaths.add(filePath);
94486
+ else fileCountFromScansWithoutPaths += scan.result.scannedFileCount ?? scan.result.project.sourceFileCount;
94487
+ }
94488
+ const totalScannedFileCount = uniqueScannedFilePaths.size + fileCountFromScansWithoutPaths;
94521
94489
  yield* log(`${highlighter.success("✔")} Scanned ${totalScannedFileCount} ${totalScannedFileCount === 1 ? "file" : "files"} in ${formatElapsedTime(totalElapsedMilliseconds)}`);
94522
94490
  if (displayDiagnostics.length > 0) {
94523
94491
  yield* log("");
@@ -94789,14 +94757,10 @@ const parseFileLineArgument = (rawArgument) => {
94789
94757
  };
94790
94758
  //#endregion
94791
94759
  //#region src/cli/utils/select-projects.ts
94792
- const discoverWorkspacePackages = (rootDirectory) => {
94793
- const hasRootPackageJson = isFile(Path.join(rootDirectory, "package.json"));
94794
- const packages = listWorkspacePackages(rootDirectory);
94795
- if (packages.length === 0 && (!hasRootPackageJson || isMonorepoRoot(rootDirectory))) return discoverReactSubprojects(rootDirectory);
94796
- return packages;
94797
- };
94798
94760
  const selectProjects = async (rootDirectory, projectFlag, skipPrompts, configProjects) => {
94799
- const packages = discoverWorkspacePackages(rootDirectory);
94761
+ const hasRootPackageJson = isFile(Path.join(rootDirectory, "package.json"));
94762
+ let packages = listWorkspacePackages(rootDirectory);
94763
+ if (packages.length === 0 && (!hasRootPackageJson || isMonorepoRoot(rootDirectory))) packages = discoverReactSubprojects(rootDirectory);
94800
94764
  if (projectFlag) return resolveProjectFlag(projectFlag, packages, rootDirectory);
94801
94765
  const configRequestedNames = (configProjects ?? []).map((requestedName) => requestedName.trim()).filter((requestedName) => requestedName.length > 0);
94802
94766
  if (configRequestedNames.length > 0) {
@@ -94925,14 +94889,6 @@ const projectManifestChanged = (rootDirectory, projectDirectory, diffInfo) => {
94925
94889
  return diffInfo.changedFiles.some((filePath) => toForwardSlashes(filePath) === manifestPath);
94926
94890
  };
94927
94891
  //#endregion
94928
- //#region src/cli/utils/should-fail-scan-gate.ts
94929
- const shouldFailScanGate = (input) => {
94930
- if (input.blockingLevel === "none") return false;
94931
- if (input.scans.some(({ result }) => hasLintHardFailure(result))) return true;
94932
- if (input.diagnosticsAreGateExempt === true) return false;
94933
- return shouldBlockCi(filterScansForSurface(input.scans, "ciFailure"), input.blockingLevel);
94934
- };
94935
- //#endregion
94936
94892
  //#region src/cli/utils/warn-deprecated-fail-on.ts
94937
94893
  const warnDeprecatedFailOn = (flags, userConfig) => {
94938
94894
  const usedFlag = flags.failOn !== void 0;
@@ -95059,11 +95015,12 @@ const finalizeScans = (input) => {
95059
95015
  }));
95060
95016
  }
95061
95017
  const blockingLevel = resolveBlockingLevel(input.flags, input.userConfig);
95062
- if (shouldFailScanGate({
95063
- scans: input.completedScans,
95064
- blockingLevel,
95065
- diagnosticsAreGateExempt: input.isScoreOnly || baselineDegraded
95066
- })) process.exitCode = 1;
95018
+ if (input.completedScans.some(({ result }) => hasLintHardFailure(result)) && blockingLevel !== "none") {
95019
+ process.exitCode = 1;
95020
+ return;
95021
+ }
95022
+ if (input.isScoreOnly || baselineDegraded) return;
95023
+ if (shouldBlockCi(filterScansForSurface(input.completedScans, "ciFailure"), blockingLevel)) process.exitCode = 1;
95067
95024
  };
95068
95025
  const buildChangedFilesDiffInfo = (changedFiles) => ({
95069
95026
  currentBranch: process.env.GITHUB_HEAD_REF?.trim() || null,
@@ -95339,13 +95296,13 @@ const inspectAction = async (directory, flags, invocationCommand = "inspect") =>
95339
95296
  completedScans.push(scanOutcome);
95340
95297
  }
95341
95298
  if (!isQuiet && isMultiProject && completedScans.length > 0) {
95342
- const showShareLink = !isShareOptedOut(completedScans, scanOptions.noScore) && !scanOptions.isCi;
95299
+ const shouldShowShareLink = !isShareOptedOut(completedScans, scanOptions.noScore) && !scanOptions.isCi;
95343
95300
  await runPromise(printMultiProjectSummary({
95344
95301
  completedScans,
95345
95302
  categoryFilters,
95346
95303
  verbose: Boolean(flags.verbose),
95347
95304
  outputDirectory: flags.outputDir,
95348
- isOffline: !showShareLink,
95305
+ isOffline: !shouldShowShareLink,
95349
95306
  projectName: Path.basename(resolvedDirectory),
95350
95307
  totalElapsedMilliseconds: performance$1.now() - scanLoopStartTime
95351
95308
  }));
@@ -96320,20 +96277,6 @@ const COMMAND_FLAG_SPECS = new Map([
96320
96277
  longOptionsWithOptionalValues: /* @__PURE__ */ new Set(),
96321
96278
  shortOptionsWithoutValues: new Set(["-h"]),
96322
96279
  shortOptionsWithRequiredValues: new Set(["-c"])
96323
- }],
96324
- ["experimental-tui", {
96325
- longOptionsWithoutValues: new Set([
96326
- "--color",
96327
- "--help",
96328
- "--no-color",
96329
- "--no-dead-code",
96330
- "--no-score",
96331
- "--yes"
96332
- ]),
96333
- longOptionsWithRequiredValues: new Set(["--blocking", "--project"]),
96334
- longOptionsWithOptionalValues: /* @__PURE__ */ new Set(),
96335
- shortOptionsWithoutValues: new Set(["-h", "-y"]),
96336
- shortOptionsWithRequiredValues: new Set(["-p"])
96337
96280
  }]
96338
96281
  ]);
96339
96282
  const isFlagLike = (argument) => argument.startsWith("-") && argument !== "-";
@@ -96513,35 +96456,6 @@ rules.command("category <category> <severity>").description("Set the severity fo
96513
96456
  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()));
96514
96457
  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()));
96515
96458
  program.command("experimental-lsp", { hidden: false }).description("[experimental] run the React Doctor language server over stdio (for editors)").allowUnknownOption().action(() => {});
96516
- 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-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) => {
96517
- const options = command.optsWithGlobals();
96518
- const deadCode = options.deadCode === false ? false : void 0;
96519
- const noScore = options.score === false ? true : void 0;
96520
- const nodeMajorVersion = Number(process.versions.node.split(".")[0]);
96521
- if (process.stdout.isTTY !== true || process.stdin.isTTY !== true || isNonInteractiveEnvironment() || nodeMajorVersion < 22) {
96522
- await inspectAction(directory, {
96523
- deadCode,
96524
- score: options.score === false ? false : void 0,
96525
- project: options.project,
96526
- yes: options.yes,
96527
- blocking: options.blocking
96528
- });
96529
- return;
96530
- }
96531
- recordCount(METRIC.cliInvoked, 1, { command: "experimental-tui" });
96532
- const { runScanApp } = await import("./run-scan-app-DZ5jGWFf.js");
96533
- const { shouldFail } = await runScanApp({
96534
- directory,
96535
- options: {
96536
- deadCode,
96537
- noScore
96538
- },
96539
- projectFlag: options.project,
96540
- skipPrompts: options.yes ?? false,
96541
- blocking: options.blocking
96542
- });
96543
- if (shouldFail) process.exitCode = 1;
96544
- });
96545
96459
  process.stdout.on("error", (error) => {
96546
96460
  if (error.code === "EPIPE") process.exit(0);
96547
96461
  });
@@ -96568,7 +96482,7 @@ Promise.resolve().then(() => assertNoRemovedFlags(process.argv)).then(() => prog
96568
96482
  handleError(error, { sentryEventId });
96569
96483
  });
96570
96484
  //#endregion
96571
- export { toForwardSlashes as A, highlighter as B, buildRulePriorityMap as C, resolveBlockingLevel as D, formatLearnMoreLine as E, isCiEnvironment as F, sync as G, mergeReactDoctorConfigs as H, DIAGNOSTIC_CATEGORY_BUCKETS as I, void_ as K, Progress as L, isUnicodeSupported as M, METRIC as N, isReactDoctorWorkflowInstalled as O, TUI_REPORT_DETAIL_WIDTH_FRACTION as P, Reporter as R, hasLintHardFailure as S, formatFixRecipeLine as T, resolveScanTarget as U, mapWithConcurrency as V, runPromise 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, inspect as p, succeed$3 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, getCategoryImpact as z };
96485
+ export {};
96572
96486
 
96573
96487
  //# sourceMappingURL=cli.js.map
96574
- //# debugId=be7f877d-9463-5212-8e1f-dc5feea97da0
96488
+ //# debugId=81e6305d-aa88-52f6-82ff-ac652d90107a
package/dist/index.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]="ef9143d9-5b2e-5eca-adb3-89fde61aafe8")}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]="4b0e44db-b753-5cee-b79d-4976faf0c4cb")}catch(e){}}();
3
3
  import { i as __toESM$1, r as __require, t as __commonJSMin$1 } from "./chunk-ChKA3wi6.js";
4
4
  import { createRequire } from "node:module";
5
5
  import reactDoctorPlugin, { ALL_REACT_DOCTOR_RULE_KEYS, CROSS_FILE_DEPENDENCY_COLLECTORS, CROSS_FILE_RULE_IDS, FRAMEWORK_SPECIFIC_RULE_KEYS, FRAMEWORK_TOKENS, MOTION_LIBRARY_PACKAGES, REACT_COMPILER_RULES, REACT_DOCTOR_RULES, classifySecurityScanFile, collectCrossFileDependencyProbes, resetManifestCaches, shouldReadSecurityScanContent } from "oxlint-plugin-react-doctor";
@@ -61762,13 +61762,6 @@ const DIAGNOSTIC_CATEGORY_BUCKETS = [
61762
61762
  "Maintainability"
61763
61763
  ];
61764
61764
  const OCCURRENCE_MATCHED_CATEGORIES = new Set(["Accessibility"]);
61765
- new Map(Object.entries({
61766
- Security: "An attacker can read user data, act as your users, or run code you never shipped.",
61767
- Bugs: "Real users hit crashes, wrong output, or state that silently corrupts.",
61768
- Performance: "Users feel extra latency and wasted renders on every interaction.",
61769
- Accessibility: "Users on screen readers, keyboards, or assistive tech get locked out.",
61770
- Maintainability: "Every future change gets slower and riskier to make."
61771
- }));
61772
61765
  const APP_ONLY_RULE_KEYS = new Set([
61773
61766
  "react-hooks-js/static-components",
61774
61767
  "react-doctor/no-render-prop-children",
@@ -73883,4 +73876,4 @@ const toJsonReport = (result, options) => buildJsonReport({
73883
73876
  export { AmbiguousProjectError, NoReactDependencyError, NotADirectoryError, PackageJsonNotFoundError, ProjectNotFoundError, ReactDoctorError, buildJsonReport, buildJsonReportError, clearCaches, defineConfig, diagnose, filterSourceFiles, getDiffInfo, hasReactRuntime, isProjectDiscoveryError, isReactDoctorError, summarizeDiagnostics, toJsonReport };
73884
73877
 
73885
73878
  //# sourceMappingURL=index.js.map
73886
- //# debugId=ef9143d9-5b2e-5eca-adb3-89fde61aafe8
73879
+ //# debugId=4b0e44db-b753-5cee-b79d-4976faf0c4cb
package/dist/lsp.js CHANGED
@@ -61815,13 +61815,6 @@ const DIAGNOSTIC_CATEGORY_BUCKETS = [
61815
61815
  "Maintainability"
61816
61816
  ];
61817
61817
  const OCCURRENCE_MATCHED_CATEGORIES = new Set(["Accessibility"]);
61818
- new Map(Object.entries({
61819
- Security: "An attacker can read user data, act as your users, or run code you never shipped.",
61820
- Bugs: "Real users hit crashes, wrong output, or state that silently corrupts.",
61821
- Performance: "Users feel extra latency and wasted renders on every interaction.",
61822
- Accessibility: "Users on screen readers, keyboards, or assistive tech get locked out.",
61823
- Maintainability: "Every future change gets slower and riskier to make."
61824
- }));
61825
61818
  const APP_ONLY_RULE_KEYS = new Set([
61826
61819
  "react-hooks-js/static-components",
61827
61820
  "react-doctor/no-render-prop-children",
@@ -75504,5 +75497,5 @@ const startLanguageServer = () => {
75504
75497
  };
75505
75498
  //#endregion
75506
75499
  export { startLanguageServer };
75507
- !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]="a35abfab-9805-5633-9c17-c7012a9dc0b3")}catch(e){}}();
75508
- //# debugId=a35abfab-9805-5633-9c17-c7012a9dc0b3
75500
+ !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]="efdb823c-9e83-5162-be2f-a67125fce48a")}catch(e){}}();
75501
+ //# debugId=efdb823c-9e83-5162-be2f-a67125fce48a