react-doctor 0.9.11 → 0.9.12-dev.05ef989

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.
Files changed (35) hide show
  1. package/README.md +1 -1
  2. package/bin/react-doctor.js +1 -9
  3. package/dist/{build-section-divider-sLcWe1hO.js → build-section-divider-BNQmYjME.js} +9 -10
  4. package/dist/{ci-DsrrD52i.js → ci-B9ir-2Up.js} +12 -12
  5. package/dist/cli.js +30 -31
  6. package/dist/{design-CP25gyyg.js → design-DrPr5wfS.js} +4 -4
  7. package/dist/{devtools-DPRVRu3S.js → devtools-D8NTVj81.js} +4 -4
  8. package/dist/{exit-gracefully-CVVkBxDS.js → exit-gracefully-C-_jVMT3.js} +6 -6
  9. package/dist/index.d.ts +38 -61
  10. package/dist/index.js +54948 -54701
  11. package/dist/{inspect-D6y8oLtg.js → inspect-BfdplEq6.js} +25 -24
  12. package/dist/{install-afMFGeTB.js → install-CcPaQeBb.js} +8 -8
  13. package/dist/{install-doctor-script-Be7Mv3-l.js → install-doctor-script-BlIhuJW6.js} +4 -4
  14. package/dist/{install-github-workflow-Dev2F6Ea.js → install-github-workflow-DbnsoR9k.js} +6 -6
  15. package/dist/{install-react-doctor-DIPVunY4.js → install-react-doctor-COZnJROS.js} +47 -45
  16. package/dist/{print-branded-header-D5jLdo4p.js → print-branded-header-B_Cgff5S.js} +4 -4
  17. package/dist/project-analysis-worker.d.ts +1 -0
  18. package/dist/project-analysis-worker.js +51900 -0
  19. package/dist/{read-object-file-CEh1CqnD.js → read-object-file-NNLnIQKt.js} +4 -4
  20. package/dist/{record-metric-CJxSHfJD.js → record-metric-fdCvvcjz.js} +2668 -5698
  21. package/dist/{report-error-CAEixMg1.js → report-error-BH7DfHGY.js} +5 -5
  22. package/dist/{report-workflow-result-C0jbKH8p.js → report-workflow-result-B3YeoXsK.js} +5 -5
  23. package/dist/{resolve-scope-DS7rcRRz.js → resolve-scope-gLUuBxjI.js} +6 -6
  24. package/dist/{rules-BJynymuU.js → rules-B5VC89MR.js} +6 -6
  25. package/dist/{run-scan-app-BGzlyiIB.js → run-scan-app-Cl2mqLLB.js} +194 -145
  26. package/dist/{scan-CgPRG6_z.js → scan-7OBudBct.js} +8 -8
  27. package/dist/semver-DFtnEmN-.js +3219 -0
  28. package/dist/{should-fail-scan-gate-4jf7ZRdL.js → should-fail-scan-gate-Dch9vbPz.js} +197 -94
  29. package/dist/{should-skip-prompts-C78r7Ww7.js → should-skip-prompts-CyNdARgB.js} +4 -4
  30. package/dist/{unref-stdin-Dc8ipkaw.js → unref-stdin-Cynhx9N1.js} +6 -12
  31. package/dist/{version-DDrwsTas.js → version-axnZCkCZ.js} +4 -4
  32. package/dist/{why-BRNrKY1b.js → why-Ducgj7Hu.js} +4 -4
  33. package/package.json +6 -12
  34. package/dist/chunk-q7NCDQ7-.js +0 -28
  35. package/dist/lsp.js +0 -81114
@@ -1,23 +1,23 @@
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]="1be0ae40-8cad-55a2-b447-69faa6b7cb0b")}catch(e){}}();
3
- import { $n as orElseSucceed, An as resolveReactDoctorCacheDir, Bt as computeConfigFingerprint, Cn as mapWithConcurrency, Ct as SHARE_BASE_URL, Dt as SidecarLintCacheEnabled, E as NANOSECONDS_PER_MILLISECOND, Et as Score, Ft as buildSkippedChecks, G as isCiOrCodingAgentEnvironment, Gn as log, Gt as discoverReactSubprojects, H as ACTION_INPUT_ENVIRONMENT_VARIABLES, Hn as Console, Ht as createNodeReadFileLinesSync, It as calculateScore, Jn as decodeUnknownSync, Jt as filterSourceFiles, Kn as warn, Kt as filterDiagnosticsForSurface, Ln as summarizeDiagnostics, Lt as canonicalizeUserRuleKey, Mn as resolveScanTarget, Mt as atomicWriteJson, Nn as restoreLegacyThrow, On as resolveGithubActionsScoreMetadata, Pn as runInspect, Pt as buildRuleDocsUrl, Qn as map, R as CliInputError, St as Reporter, Tn as mergeReactDoctorConfigs, U as detectRunnerOs, Un as error, Ut as createOxlintSpawnSlots, Vn as yieldToEventLoop, Vt as computeDiagnosticDelta, W as isCiEnvironment, X as CI_URL, Xn as gen, Zn as makeSpan, _ as makeNoopConsole, _r as ParentSpan, _t as OxlintConcurrency, an as hasPublishedFixRecipe, ar as runSync, at as Files, bn as listSourceFiles, bt as Progress, c as setSentryProjectInfo, cn as highlighter, ct as Git, d as isJsonModeActive, dn as isFile, er as promise, et as Config, fn as isMonorepoRoot, fr as succeed, ft as LintPartialFailures, g as VERSION, gn as isReactDoctorError, gr as void_, gt as OXLINT_NODE_REQUIREMENT, hr as fail, ht as NodeResolver, in as groupBy, ir as runPromise, it as ENTERPRISE_CONTACT_URL, jn as resolveScanConcurrency, k as SCAN_RESULT_CACHE_FILENAME, kn as resolveLintBatchOrdering, kt as SupplyChain, l as getRunId, ln as isDirectory, lt as HTML_FILE_PATTERN, mt as MILLISECONDS_PER_SECOND, n as recordDistribution, nr as provideContext, o as buildSentryScope, or as sync, pt as Linter, qn as Diagnostic, qt as filterPathsOutsideDirectories, r as toSpanAttributes, rr as provideService, rt as DeadCode, s as buildSentryProjectContext, sn as hashFileContents, sr as try_, st as GIT_SHOW_MAX_BUFFER_BYTES, t as recordCount, tn as getDiagnosticRuleIdentity, tr as provide, tt as DIAGNOSTIC_CATEGORY_BUCKETS, ur as mergeAll, ut as JSX_FILE_PATTERN, vn as isScanComplete, vt as OxlintSpawnSlots, w as METRIC, wn as materializeSourceTree, x as BASELINE_FILES_TEMP_DIR_PREFIX, xn as listWorkspacePackages, xt as Project, yt as PerFileLintCacheEnabled, z as isNonInteractiveEnvironment, zt as collectSourceFileCountsByDirectory } from "./record-metric-CJxSHfJD.js";
4
- import { a as setActiveRunTrace, i as recordRunTraceId, o as getTelemetryContext } from "./unref-stdin-Dc8ipkaw.js";
5
- import { n as buildFooterLinkLines, r as activeScanAbortRegistry, t as buildSectionDivider } from "./build-section-divider-sLcWe1hO.js";
6
- import { D as getPackageJsonPath, L as cliLogger, O as isRecord, P as runGit, a as installReactDoctorScriptStep, c as spinner, n as findNearestPackageDirectory, o as isSpinnerSilent, s as setSpinnerSilent } from "./install-doctor-script-Be7Mv3-l.js";
7
- import { d as detectDefaultBranch, l as prompts, r as installReactDoctorWorkflow, u as isCommandAvailable } from "./install-github-workflow-Dev2F6Ea.js";
8
- import { a as isValidBlockingLevel, c as openWorkflowPullRequest, l as stageWorkflowFile, o as pickBlockingLevel, u as toForwardSlashes } from "./resolve-scope-DS7rcRRz.js";
9
- import { h as detectAvailableAgents, t as reportWorkflowResult } from "./report-workflow-result-C0jbKH8p.js";
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]="19bff337-5a1f-5558-b6b7-bd4274f53f38")}catch(e){}}();
3
+ import { $ as Maintainability, $t as listSourceFiles, An as makeSpan, At as filterDiagnosticsForSurface, Bn as try_, Ct as computeConfigFingerprint, Dn as decodeUnknownSync, Dt as createOxlintSpawnSlots, E as NANOSECONDS_PER_MILLISECOND, En as Diagnostic, Et as createNodeReadFileLinesSync, Fn as provideContext, G as isCiOrCodingAgentEnvironment, Gn as succeed, Gt as highlighter, H as ACTION_INPUT_ENVIRONMENT_VARIABLES, Ht as hasPublishedFixRecipe, In as provideService, J as Files, Jn as fail, Kt as isMonorepoRoot, Ln as runPromise, Mn as orElseSucceed, Mt as filterSourceFiles, Nn as promise, Pn as provide, Q as Linter, R as CliInputError, Rn as runSync, Rt as getDiagnosticRuleIdentity, Sn as error, St as collectSourceFileCountsByDirectory, Tn as warn, Tt as countOptInProjectRuleSelections, U as detectRunnerOs, Un as mergeAll, Vt as groupBy, W as isCiEnvironment, Wt as hashFileContents, Xn as ParentSpan, Y as Git, Yn as void_, Yt as isReactDoctorError, Z as LintPartialFailures, Zt as isScanComplete, _ as makeNoopConsole, _n as summarizeDiagnostics, _t as buildSkippedChecks, an as mergeReactDoctorConfigs, at as Project, bn as yieldToEventLoop, br as isFile, bt as classifyFileContext, c as setSentryProjectInfo, cn as resolveLintBatchOrdering, cr as GIT_SHOW_MAX_BUFFER_BYTES, ct as SidecarLintCacheEnabled, d as isJsonModeActive, dn as resolveScanTarget, dr as JSX_FILE_PATTERN, en as listSourceFilesCooperative, er as CI_URL, et as NodeResolver, fn as restoreLegacyThrow, fr as MILLISECONDS_PER_SECOND, g as VERSION, gn as shouldUseMaintainabilityLayer, gt as buildRuleSeverityControls, ht as buildRuleDocsUrl, in as materializeSourceTree, ir as DIAGNOSTIC_CATEGORY_BUCKETS, it as Progress, jn as map, jt as filterPathsOutsideDirectories, k as SCAN_RESULT_CACHE_FILENAME, kn as gen, kt as discoverSupportedSubprojects, l as getRunId, ln as resolveReactDoctorCacheDir, lr as HTML_FILE_PATTERN, mr as SHARE_BASE_URL, n as recordDistribution, nt as OxlintSpawnSlots, o as buildSentryScope, on as remainingDeadlineBudgetMs, or as ENTERPRISE_CONTACT_URL, ot as Reporter, pn as runInspect, pr as OXLINT_NODE_REQUIREMENT, pt as atomicWriteJson, q as Config, qt as isPathInsideDirectory, r as toSpanAttributes, rn as mapWithConcurrency, rt as PerFileLintCacheEnabled, s as buildSentryProjectContext, sn as resolveGithubActionsScoreMetadata, st as Score, t as recordCount, tn as listWorkspacePackages, tt as OxlintConcurrency, un as resolveScanConcurrency, ur as JSX_DUPLICATION_SOURCE_FILE_PATTERN, ut as SupplyChain, vr as isDirectory, vt as calculateScore, w as METRIC, wn as log, wt as computeDiagnosticDelta, x as BASELINE_FILES_TEMP_DIR_PREFIX, xn as Console, yt as canonicalizeUserRuleKey, z as isNonInteractiveEnvironment, zn as sync } from "./record-metric-fdCvvcjz.js";
4
+ import { a as setActiveRunTrace, i as recordRunTraceId, o as getTelemetryContext } from "./unref-stdin-Cynhx9N1.js";
5
+ import { n as buildFooterLinkLines, r as activeScanAbortRegistry, t as buildSectionDivider } from "./build-section-divider-BNQmYjME.js";
6
+ import { D as getPackageJsonPath, L as cliLogger, O as isRecord, P as runGit, a as installReactDoctorScriptStep, c as spinner, n as findNearestPackageDirectory, o as isSpinnerSilent, s as setSpinnerSilent } from "./install-doctor-script-BlIhuJW6.js";
7
+ import { d as detectDefaultBranch, l as prompts, r as installReactDoctorWorkflow, u as isCommandAvailable } from "./install-github-workflow-DbnsoR9k.js";
8
+ import { a as isValidBlockingLevel, c as openWorkflowPullRequest, l as stageWorkflowFile, o as pickBlockingLevel, u as toForwardSlashes } from "./resolve-scope-gLUuBxjI.js";
9
+ import { h as detectAvailableAgents, t as reportWorkflowResult } from "./report-workflow-result-B3YeoXsK.js";
10
10
  import { createRequire } from "node:module";
11
11
  import { spawn } from "node:child_process";
12
12
  import * as path$1 from "node:path";
13
13
  import path from "node:path";
14
- import * as fs$1 from "node:fs";
14
+ import * as fs$2 from "node:fs";
15
15
  import fs, { mkdtempSync, rmSync } from "node:fs";
16
- import crypto, { randomUUID } from "node:crypto";
17
16
  import ts from "typescript";
17
+ import crypto, { randomUUID } from "node:crypto";
18
18
  import { tmpdir } from "node:os";
19
+ import * as fs$1 from "node:fs/promises";
19
20
  import { performance } from "node:perf_hooks";
20
- import { codeFrameColumns } from "@babel/code-frame";
21
21
  //#region src/cli/utils/apply-observability.ts
22
22
  /**
23
23
  * Installs the tracing backend for the inspect program.
@@ -84,7 +84,10 @@ const buildSpinnerProgressHandle = (text) => {
84
84
  */
85
85
  const buildRuntimeLayers = (input) => {
86
86
  const linterLayer = input.shouldSkipLint ? Linter.layerOf([]) : Linter.layerOxlint;
87
- const deadCodeLayer = input.shouldRunDeadCode ? DeadCode.layerNode : DeadCode.layerOf([]);
87
+ const maintainabilityLayer = shouldUseMaintainabilityLayer({
88
+ shouldRunDuplicateJsx: input.shouldRunDeadCode,
89
+ userConfig: input.userConfig
90
+ }) ? Maintainability.layerNode : Maintainability.layerOf([]);
88
91
  const scoreLayer = input.shouldComputeScore ? Score.layerHttp : Score.layerOf(null);
89
92
  const supplyChainLayer = input.shouldRunSupplyChain ? SupplyChain.layerNode : SupplyChain.layerOf([]);
90
93
  const projectLayer = input.projectInfoOverride === void 0 ? Project.layerNode : Project.layerOf(input.projectInfoOverride);
@@ -94,7 +97,7 @@ const buildRuntimeLayers = (input) => {
94
97
  config: input.userConfig,
95
98
  resolvedDirectory: input.directory,
96
99
  configSourceDirectory: input.configSourceDirectory
97
- }) : Config.layerNode, Files.layerNode, Git.layerNode, linterLayer, LintPartialFailures.layerLive, deadCodeLayer, progressLayer, reporterLayer, scoreLayer, supplyChainLayer);
100
+ }) : Config.layerNode, Files.layerNode, Git.layerNode, linterLayer, LintPartialFailures.layerLive, maintainabilityLayer, progressLayer, reporterLayer, scoreLayer, supplyChainLayer);
98
101
  const layersWithConcurrency = input.oxlintConcurrency === void 0 ? baseLayers : mergeAll(baseLayers, succeed(OxlintConcurrency, input.oxlintConcurrency));
99
102
  return input.oxlintSpawnSlots === void 0 ? layersWithConcurrency : mergeAll(layersWithConcurrency, succeed(OxlintSpawnSlots, input.oxlintSpawnSlots));
100
103
  };
@@ -310,7 +313,7 @@ const recordScanMetrics = (input) => {
310
313
  recordCount(METRIC.scanCompleted, 1, {
311
314
  mode: input.mode,
312
315
  lint: input.lint,
313
- deadCode: input.deadCode,
316
+ maintainability: input.deadCode,
314
317
  parallel: input.parallel,
315
318
  scoreOnly: input.scoreOnly,
316
319
  didLintFail: input.didLintFail,
@@ -350,7 +353,7 @@ const recordScanMetrics = (input) => {
350
353
  else if (!input.noScore && !input.didLintFail && !input.didDeadCodeFail) recordCount(METRIC.scoreUnavailable, 1, { mode: input.mode });
351
354
  if (input.baselineDegraded) recordCount(METRIC.baselineDegraded, 1);
352
355
  if (input.didLintFail) recordCount(METRIC.lintFailed, 1, { reasonKind: input.lintFailureReasonKind });
353
- if (input.didDeadCodeFail) recordCount(METRIC.deadCodeFailed, 1);
356
+ if (input.didDeadCodeFail) recordCount(METRIC.maintainabilityFailed, 1);
354
357
  for (const check of result.skippedChecks) recordCount(METRIC.scanCheckSkipped, 1, {
355
358
  check,
356
359
  reason: result.skippedCheckReasons?.[check] ?? null
@@ -494,10 +497,16 @@ const CACHE_DISABLED_VALUES = new Set(["1", "true"]);
494
497
  const TOOLCHAIN_PACKAGE_SPECIFIERS = [
495
498
  "oxlint/package.json",
496
499
  "oxlint-plugin-react-doctor/package.json",
497
- "deslop-js/package.json",
498
500
  "eslint-plugin-react-hooks/package.json"
499
501
  ];
500
502
  const bundledRequire = createRequire(import.meta.url);
503
+ const RULE_PLUGIN_CONTENT_FINGERPRINT = (() => {
504
+ try {
505
+ return `oxlint-plugin-react-doctor#fingerprint=${hashFileContents(bundledRequire.resolve("oxlint-plugin-react-doctor")) ?? "unreadable"}`;
506
+ } catch {
507
+ return "oxlint-plugin-react-doctor#fingerprint=unresolved";
508
+ }
509
+ })();
501
510
  const normalizeForStableJson = (value) => {
502
511
  if (value === null) return null;
503
512
  if (value === void 0) return void 0;
@@ -633,12 +642,12 @@ const resolveCacheFilePath = (projectDirectory) => path.join(resolveReactDoctorC
633
642
  const readPersistedCache = (cacheFilePath) => {
634
643
  try {
635
644
  const parsed = JSON.parse(fs.readFileSync(cacheFilePath, "utf8"));
636
- if (!isRecord(parsed) || parsed.version !== 6) return {
637
- version: 6,
645
+ if (!isRecord(parsed) || parsed.version !== 7) return {
646
+ version: 7,
638
647
  entries: []
639
648
  };
640
649
  if (!Array.isArray(parsed.entries)) return {
641
- version: 6,
650
+ version: 7,
642
651
  entries: []
643
652
  };
644
653
  const entries = [];
@@ -653,12 +662,12 @@ const readPersistedCache = (cacheFilePath) => {
653
662
  });
654
663
  }
655
664
  return {
656
- version: 6,
665
+ version: 7,
657
666
  entries
658
667
  };
659
668
  } catch {
660
669
  return {
661
- version: 6,
670
+ version: 7,
662
671
  entries: []
663
672
  };
664
673
  }
@@ -666,7 +675,7 @@ const readPersistedCache = (cacheFilePath) => {
666
675
  /**
667
676
  * The global cache off-switch (`REACT_DOCTOR_NO_CACHE`), which disables every
668
677
  * cache subsystem: this whole-repo scan cache plus core's per-file lint,
669
- * sidecar, and dead-code caches (their `Context.Reference` defaults read the
678
+ * sidecar caches (their `Context.Reference` defaults read the
670
679
  * same variable). Exported for the wide event's `cache.temperature`
671
680
  * derivation, which reports `"disabled"` instead of `"cold"` when the switch
672
681
  * is on. Granular knobs (`REACT_DOCTOR_NO_FILE_CACHE`, …) are deliberately
@@ -689,7 +698,7 @@ const fileFingerprint = (filePath) => {
689
698
  return null;
690
699
  }
691
700
  };
692
- const resolveToolchainFingerprint = (nodeBinaryPath) => {
701
+ const resolveScanResultToolchainFingerprint = (nodeBinaryPath) => {
693
702
  const fingerprints = [];
694
703
  if (nodeBinaryPath !== null) fingerprints.push(nodeBinaryPath === process.execPath ? `node=${process.version}` : `node=${fileFingerprint(nodeBinaryPath) ?? "unreadable"}`);
695
704
  for (const specifier of TOOLCHAIN_PACKAGE_SPECIFIERS) try {
@@ -699,6 +708,7 @@ const resolveToolchainFingerprint = (nodeBinaryPath) => {
699
708
  } catch {
700
709
  fingerprints.push(`${specifier}=missing`);
701
710
  }
711
+ fingerprints.push(RULE_PLUGIN_CONTENT_FINGERPRINT);
702
712
  return fingerprints;
703
713
  };
704
714
  const buildScanResultCacheKey = (input) => {
@@ -708,14 +718,14 @@ const buildScanResultCacheKey = (input) => {
708
718
  if (repositoryIdentity === null) return null;
709
719
  if (stringifyStableJson(input.userConfig) === null) return null;
710
720
  const cacheKeyJson = stringifyStableJson({
711
- schemaVersion: 6,
721
+ schemaVersion: 7,
712
722
  projectIdentity: resolveProjectIdentity(input.projectDirectory),
713
723
  headSha: repositoryIdentity.headSha,
714
724
  worktreeFingerprint: repositoryIdentity.worktreeFingerprint,
715
725
  dotenvFingerprint: resolveDotenvFingerprint(input.projectDirectory),
716
726
  reactDoctorVersion: input.version,
717
727
  nodeVersion: process.version,
718
- toolchainFingerprint: resolveToolchainFingerprint(input.nodeBinaryPath),
728
+ toolchainFingerprint: resolveScanResultToolchainFingerprint(input.nodeBinaryPath),
719
729
  configFingerprint: computeConfigFingerprint(input.projectDirectory, input.version),
720
730
  hasConfigOverride: input.hasConfigOverride,
721
731
  configSourceDirectory: input.configSourceDirectory,
@@ -756,7 +766,7 @@ const createScanResultCache = (projectDirectory) => {
756
766
  for (const entry of readPersistedCache(cacheFilePath).entries) mergedEntries.set(entry.key, entry);
757
767
  for (const entry of entries.values()) mergedEntries.set(entry.key, entry);
758
768
  atomicWriteJson(cacheFilePath, {
759
- version: 6,
769
+ version: 7,
760
770
  entries: [...mergedEntries.values()].sort((firstEntry, secondEntry) => secondEntry.createdAtMs - firstEntry.createdAtMs).slice(0, 20)
761
771
  });
762
772
  };
@@ -791,8 +801,7 @@ const shouldBlockCi = (diagnostics, blockingLevel) => {
791
801
  /**
792
802
  * Lowercase, key-safe form of a rule category for the `diag.category.*`
793
803
  * telemetry attribute namespace (categories carry spaces / capitals, e.g.
794
- * "Performance" → `performance`, "Dead Code" → `dead_code`). Shared by the
795
- * CLI run event and the editor LSP wide event so both namespace identically.
804
+ * "Performance" → `performance`, "Dead Code" → `dead_code`).
796
805
  */
797
806
  const toCategoryKey = (category) => category.toLowerCase().replace(/[^a-z0-9]+/g, "_");
798
807
  //#endregion
@@ -822,12 +831,6 @@ const readEnvBoolean = (name) => {
822
831
  return value.toLowerCase() === "true" || value === "1";
823
832
  };
824
833
  const ratioOf = (numerator, denominator) => denominator != null && denominator > 0 ? (numerator ?? 0) / denominator : null;
825
- const resolveDeadCodeReuseRatio = (result) => {
826
- if (result.deadCodeCacheHit === true) return 1;
827
- const summaryTotal = (result.deadCodeSummaryCacheHits ?? 0) + (result.deadCodeSummaryCacheMisses ?? 0);
828
- if (summaryTotal > 0) return (result.deadCodeSummaryCacheHits ?? 0) / summaryTotal;
829
- return result.deadCodeCacheHit === false ? 0 : null;
830
- };
831
834
  /**
832
835
  * One queryable cache temperature per scan, derived from the whole stack:
833
836
  *
@@ -835,8 +838,7 @@ const resolveDeadCodeReuseRatio = (result) => {
835
838
  * `wholeRepoCacheHit` flag from the CLI's cachedPayload
836
839
  * branch; no scan work ran).
837
840
  * - `"warm"` — any incremental reuse: per-file lint hits, sidecar
838
- * replays, a dead-code whole-result hit, or dead-code
839
- * summary-cache hits.
841
+ * replays.
840
842
  * - `"disabled"` — zero reuse because the global `REACT_DOCTOR_NO_CACHE`
841
843
  * off-switch is on. Granular per-cache opt-outs
842
844
  * (`REACT_DOCTOR_NO_FILE_CACHE`, …) still read warm/cold,
@@ -845,7 +847,7 @@ const resolveDeadCodeReuseRatio = (result) => {
845
847
  *
846
848
  * `cache.warmth` is the headline reuse magnitude in [0, 1]: the plain mean of
847
849
  * the subsystem reuse fractions known this run (lint hit ratio, sidecar
848
- * replay ratio, dead-code reuse), skipping subsystems that never consulted a
850
+ * replay ratio), skipping subsystems that never consulted a
849
851
  * cache; `1` on turbo, dropped when nothing consulted any cache. Deliberately
850
852
  * unweighted — the per-subsystem dims stay the precise signal; warmth is the
851
853
  * p50/p90-able summary. Emitted only on the success path.
@@ -858,11 +860,7 @@ const buildCacheAttributes = (input) => {
858
860
  warmth: 1
859
861
  });
860
862
  const result = input.result;
861
- const subsystemReuseRatios = [
862
- ratioOf(result.lintCacheHitFileCount, result.lintCacheTotalFileCount),
863
- ratioOf(result.lintSidecarReplayedFileCount, result.lintSidecarTotalFileCount),
864
- resolveDeadCodeReuseRatio(result)
865
- ];
863
+ const subsystemReuseRatios = [ratioOf(result.lintCacheHitFileCount, result.lintCacheTotalFileCount), ratioOf(result.lintSidecarReplayedFileCount, result.lintSidecarTotalFileCount)];
866
864
  let knownSubsystemCount = 0;
867
865
  let reuseRatioSum = 0;
868
866
  for (const reuseRatio of subsystemReuseRatios) {
@@ -1014,13 +1012,7 @@ const buildOutcomeAttributes = (input) => {
1014
1012
  sidecarTotalFiles: result.lintSidecarTotalFileCount,
1015
1013
  sidecarReplayRatio: ratioOf(result.lintSidecarReplayedFileCount, result.lintSidecarTotalFileCount)
1016
1014
  }),
1017
- ...withNamespace("deadCode", {
1018
- failed: input.didDeadCodeFail,
1019
- overlapped: input.deadCodeOverlapped,
1020
- cacheHit: result.deadCodeCacheHit,
1021
- summaryCacheHits: result.deadCodeSummaryCacheHits,
1022
- summaryCacheMisses: result.deadCodeSummaryCacheMisses
1023
- }),
1015
+ ...withNamespace("maintainability", { failed: input.didDeadCodeFail }),
1024
1016
  ...withNamespace("supplyChain", { overlapTimedOut: input.supplyChainOverlapTimedOut }),
1025
1017
  ...withNamespace("securityScan", { failed: input.securityScanFailed }),
1026
1018
  ...withNamespace("timing", {
@@ -1063,7 +1055,7 @@ const buildScanAttributes = (input) => {
1063
1055
  workerCount: input.workerCount,
1064
1056
  maxDurationMs: input.maxDurationMs,
1065
1057
  lint: input.lint,
1066
- deadCode: input.deadCode,
1058
+ maintainability: input.deadCode,
1067
1059
  supplyChain: input.supplyChain,
1068
1060
  scoreOnly: input.scoreOnly,
1069
1061
  noScore: input.noScore,
@@ -1074,6 +1066,7 @@ const buildScanAttributes = (input) => {
1074
1066
  hasCustomConfig: input.hasCustomConfig,
1075
1067
  rulesConfigured: ruleKeys.length,
1076
1068
  rulesDisabled: ruleKeys.filter((key) => ruleOverrides[key] === "off").length,
1069
+ projectAnalysisRuleCount: countOptInProjectRuleSelections(buildRuleSeverityControls(input.userConfig)),
1077
1070
  fileCount: input.result?.scannedFileCount,
1078
1071
  nonJsxFileCount: input.result?.analyzedFiles?.filter((filePath) => !JSX_FILE_PATTERN.test(filePath)).length,
1079
1072
  htmlFileCount: input.result?.analyzedFiles?.filter((filePath) => HTML_FILE_PATTERN.test(filePath)).length,
@@ -1084,7 +1077,7 @@ const buildScanAttributes = (input) => {
1084
1077
  * Projects a scan into the namespaced attribute set for its root span — the
1085
1078
  * canonical per-scan "wide event". Every attribute carries a dotted namespace
1086
1079
  * that groups it by concept (`scan.*` config, `action.*` CI knobs, `outcome.*`
1087
- * verdict, `diag.*` findings, `score.*`, `lint.*`, `deadCode.*`, `cache.*`,
1080
+ * verdict, `diag.*` findings, `score.*`, `lint.*`, `maintainability.*`, `cache.*`,
1088
1081
  * `supplyChain.*`, `timing.*`, `migration.*`, `baseline.*`) so the attributes
1089
1082
  * tree up in Sentry's attribute browser and stay filter-/group-/aggregate-able
1090
1083
  * in the Spans dataset. Pure and exported so the projection (outcome
@@ -1323,7 +1316,7 @@ const buildNoScoreMessage = (input) => {
1323
1316
  reason = input.disabledMessage ?? "Score disabled by --no-score.";
1324
1317
  break;
1325
1318
  case "analysis-incomplete":
1326
- reason = "Score not shown because lint or dead-code analysis could not complete.";
1319
+ reason = "Score not shown because lint or maintainability analysis could not complete.";
1327
1320
  break;
1328
1321
  case "api-unavailable":
1329
1322
  reason = "Score unavailable (could not reach the score API).";
@@ -1371,7 +1364,7 @@ const formatRuleSummary = (ruleKey, ruleDiagnostics) => {
1371
1364
  const ruleDumpFileName = (ruleKey) => ruleKey.replace(/\//g, "--") + ".txt";
1372
1365
  const readPreviousRuleDumpFileNames = (directory) => {
1373
1366
  try {
1374
- const previous = JSON.parse(fs$1.readFileSync(path$1.join(directory, "diagnostics.json"), "utf8"));
1367
+ const previous = JSON.parse(fs$2.readFileSync(path$1.join(directory, "diagnostics.json"), "utf8"));
1375
1368
  return new Set(previous.filter((d) => typeof d?.plugin === "string" && typeof d?.rule === "string").map((d) => ruleDumpFileName(`${d.plugin}/${d.rule}`)));
1376
1369
  } catch {
1377
1370
  return /* @__PURE__ */ new Set();
@@ -1379,10 +1372,10 @@ const readPreviousRuleDumpFileNames = (directory) => {
1379
1372
  };
1380
1373
  const writeDiagnosticsDirectory = (diagnostics, outputDirectory) => {
1381
1374
  const directory = outputDirectory ? path$1.resolve(outputDirectory) : path$1.join(tmpdir(), `react-doctor-${randomUUID()}`);
1382
- if (outputDirectory) for (const fileName of readPreviousRuleDumpFileNames(directory)) fs$1.rmSync(path$1.join(directory, fileName), { force: true });
1383
- fs$1.mkdirSync(directory, { recursive: true });
1384
- for (const [ruleKey, ruleDiagnostics] of buildSortedRuleGroups(diagnostics)) fs$1.writeFileSync(path$1.join(directory, ruleDumpFileName(ruleKey)), formatRuleSummary(ruleKey, ruleDiagnostics));
1385
- fs$1.writeFileSync(path$1.join(directory, "diagnostics.json"), JSON.stringify(diagnostics));
1375
+ if (outputDirectory) for (const fileName of readPreviousRuleDumpFileNames(directory)) fs$2.rmSync(path$1.join(directory, fileName), { force: true });
1376
+ fs$2.mkdirSync(directory, { recursive: true });
1377
+ for (const [ruleKey, ruleDiagnostics] of buildSortedRuleGroups(diagnostics)) fs$2.writeFileSync(path$1.join(directory, ruleDumpFileName(ruleKey)), formatRuleSummary(ruleKey, ruleDiagnostics));
1378
+ fs$2.writeFileSync(path$1.join(directory, "diagnostics.json"), JSON.stringify(diagnostics));
1386
1379
  return directory;
1387
1380
  };
1388
1381
  //#endregion
@@ -1426,6 +1419,9 @@ const formatElapsedTime = (elapsedMilliseconds) => {
1426
1419
  return `${(elapsedMilliseconds / MILLISECONDS_PER_SECOND).toFixed(1)}s`;
1427
1420
  };
1428
1421
  //#endregion
1422
+ //#region src/cli/utils/format-skipped-check-label.ts
1423
+ const formatSkippedCheckLabel = (skippedCheck) => skippedCheck === "dead-code" ? "maintainability" : skippedCheck;
1424
+ //#endregion
1429
1425
  //#region src/cli/utils/pluralize.ts
1430
1426
  const pluralize = (count, singularNoun) => `${count} ${singularNoun}${count === 1 ? "" : "s"}`;
1431
1427
  //#endregion
@@ -1474,7 +1470,7 @@ const printHeadlessReport = (input) => gen(function* () {
1474
1470
  }
1475
1471
  if (input.skippedChecks.length > 0) {
1476
1472
  yield* log("");
1477
- yield* warn(highlighter.warn(`Results are incomplete: ${input.skippedChecks.join(" and ")} checks failed.`));
1473
+ yield* warn(highlighter.warn(`Results are incomplete: ${input.skippedChecks.map(formatSkippedCheckLabel).join(" and ")} checks failed.`));
1478
1474
  }
1479
1475
  });
1480
1476
  //#endregion
@@ -1674,12 +1670,39 @@ const renderAndRecordScan = async (input) => {
1674
1670
  didDeadCodeFail: input.payload.didDeadCodeFail,
1675
1671
  supplyChainOverlapTimedOut: input.payload.supplyChainOverlapTimedOut,
1676
1672
  securityScanFailed: input.payload.securityScanFailed,
1677
- deadCodeOverlapped: input.payload.deadCodeOverlapped,
1678
1673
  suppressedRuleCounts: input.payload.suppressedRuleCounts ?? []
1679
1674
  });
1680
1675
  return result;
1681
1676
  };
1682
1677
  //#endregion
1678
+ //#region src/cli/utils/copy-unchanged-baseline-sources.ts
1679
+ const copyUnchangedBaselineSources = async (input) => {
1680
+ const baseMaterializedFiles = new Set(input.baseMaterializedFiles.map(toForwardSlashes));
1681
+ const headChangedFiles = new Set(input.headChangedFiles.map(toForwardSlashes));
1682
+ const untrackedFiles = new Set(input.untrackedFiles.map(toForwardSlashes));
1683
+ const sourceDirectory = path$1.resolve(input.directory);
1684
+ const targetDirectory = path$1.resolve(input.tempDirectory);
1685
+ return (await mapWithConcurrency(input.sourceFiles.filter((filePath) => {
1686
+ const normalizedPath = toForwardSlashes(filePath);
1687
+ return !baseMaterializedFiles.has(normalizedPath) && !headChangedFiles.has(normalizedPath) && !untrackedFiles.has(normalizedPath);
1688
+ }), 32, async (filePath) => {
1689
+ input.signal?.throwIfAborted();
1690
+ if (input.deadlineEpochMs !== null && Date.now() >= input.deadlineEpochMs) return false;
1691
+ const sourcePath = path$1.resolve(sourceDirectory, filePath);
1692
+ const targetPath = path$1.resolve(targetDirectory, filePath);
1693
+ if (!isPathInsideDirectory(sourcePath, sourceDirectory) || !isPathInsideDirectory(targetPath, targetDirectory)) return false;
1694
+ try {
1695
+ if (!(await fs$1.lstat(sourcePath)).isFile()) return false;
1696
+ await fs$1.mkdir(path$1.dirname(targetPath), { recursive: true });
1697
+ await fs$1.copyFile(sourcePath, targetPath);
1698
+ return true;
1699
+ } catch {
1700
+ input.signal?.throwIfAborted();
1701
+ return false;
1702
+ }
1703
+ })).every(Boolean);
1704
+ };
1705
+ //#endregion
1683
1706
  //#region src/cli/utils/read-diagnostic-evidence.ts
1684
1707
  const SOURCE_EXTENSIONS = [
1685
1708
  ".ts",
@@ -2097,7 +2120,6 @@ const resolveMergeBaseRef = async (directory, ref) => {
2097
2120
  const silentConsole$1 = makeNoopConsole();
2098
2121
  const countIncompleteLintFiles = (lintPartialFailures) => countDroppedLintFiles(lintPartialFailures) + countDeadlineSkippedFiles(lintPartialFailures);
2099
2122
  const runBaselineComparison = async (input) => {
2100
- const temporaryDirectory = mkdtempSync(path.join(tmpdir(), BASELINE_FILES_TEMP_DIR_PREFIX));
2101
2123
  const baselineIncludePaths = filterPathsOutsideDirectories({
2102
2124
  rootDirectory: input.directory,
2103
2125
  relativePaths: input.options.includePaths,
@@ -2113,12 +2135,24 @@ const runBaselineComparison = async (input) => {
2113
2135
  relativePaths: input.headFiles,
2114
2136
  excludedDirectories: input.options.excludedProjectDirectories
2115
2137
  }) : void 0;
2138
+ const remainingBaselineBudgetMs = input.deadlineEpochMs === null ? null : remainingDeadlineBudgetMs(input.deadlineEpochMs);
2139
+ if (remainingBaselineBudgetMs === 0) return null;
2140
+ const baselineDeadlineSignal = remainingBaselineBudgetMs === null ? void 0 : AbortSignal.timeout(remainingBaselineBudgetMs);
2141
+ const baselineListingSignal = baselineDeadlineSignal === void 0 ? input.oxlintRuntime.abortSignal : input.oxlintRuntime.abortSignal === void 0 ? baselineDeadlineSignal : AbortSignal.any([input.oxlintRuntime.abortSignal, baselineDeadlineSignal]);
2142
+ let maintainabilitySourceFiles = [];
2143
+ if (input.options.deadCode) try {
2144
+ maintainabilitySourceFiles = (await listSourceFilesCooperative(input.directory, baselineListingSignal)).filter((filePath) => JSX_DUPLICATION_SOURCE_FILE_PATTERN.test(filePath) && classifyFileContext(filePath) === "production");
2145
+ } catch (error) {
2146
+ if (baselineDeadlineSignal?.aborted) return null;
2147
+ throw error;
2148
+ }
2149
+ const temporaryDirectory = mkdtempSync(path.join(tmpdir(), BASELINE_FILES_TEMP_DIR_PREFIX));
2116
2150
  const snapshot = await materializeBaselineFiles({
2117
2151
  directory: input.directory,
2118
2152
  ref: input.baselineRef,
2119
- files: baselineIncludePaths,
2120
- baseFiles: baselineBaseFiles,
2121
- headFiles: baselineHeadFiles,
2153
+ files: input.options.deadCode ? input.options.includePaths : baselineIncludePaths,
2154
+ baseFiles: input.options.deadCode ? input.baseFiles : baselineBaseFiles,
2155
+ headFiles: input.options.deadCode ? input.headFiles : baselineHeadFiles,
2122
2156
  tempDirectory: temporaryDirectory
2123
2157
  }).catch((error) => {
2124
2158
  rmSync(temporaryDirectory, {
@@ -2136,22 +2170,47 @@ const runBaselineComparison = async (input) => {
2136
2170
  }
2137
2171
  try {
2138
2172
  if (!snapshot.isComplete) return null;
2173
+ if (input.options.deadCode && !await copyUnchangedBaselineSources({
2174
+ directory: input.directory,
2175
+ sourceFiles: maintainabilitySourceFiles,
2176
+ baseMaterializedFiles: snapshot.materializedFiles,
2177
+ headChangedFiles: snapshot.headFiles,
2178
+ untrackedFiles: snapshot.untrackedFiles,
2179
+ tempDirectory: snapshot.tempDirectory,
2180
+ deadlineEpochMs: input.deadlineEpochMs,
2181
+ signal: input.oxlintRuntime.abortSignal
2182
+ })) return null;
2183
+ const filteredSnapshotBaseFiles = filterPathsOutsideDirectories({
2184
+ rootDirectory: input.directory,
2185
+ relativePaths: snapshot.baseFiles,
2186
+ excludedDirectories: input.options.excludedProjectDirectories
2187
+ });
2188
+ const filteredSnapshotHeadFiles = filterPathsOutsideDirectories({
2189
+ rootDirectory: input.directory,
2190
+ relativePaths: snapshot.headFiles,
2191
+ excludedDirectories: input.options.excludedProjectDirectories
2192
+ });
2139
2193
  const analyzedHeadFiles = new Set(input.headAnalyzedFiles.map(toForwardSlashes));
2140
- const baseFiles = new Set(snapshot.baseFiles.map(toForwardSlashes));
2141
- const expectedHeadFiles = new Set(snapshot.headFiles.map(toForwardSlashes));
2194
+ const baseFiles = new Set((baselineBaseFiles ?? filteredSnapshotBaseFiles).map(toForwardSlashes));
2195
+ const expectedHeadFiles = new Set((baselineHeadFiles ?? filteredSnapshotHeadFiles).map(toForwardSlashes));
2142
2196
  for (const filePath of baselineIncludePaths) {
2143
2197
  const normalizedFilePath = toForwardSlashes(filePath);
2144
2198
  if (!baseFiles.has(normalizedFilePath)) expectedHeadFiles.add(normalizedFilePath);
2145
2199
  }
2146
- if (filterSourceFiles([...expectedHeadFiles]).some((filePath) => !analyzedHeadFiles.has(filePath))) return null;
2200
+ if (input.options.lint && filterSourceFiles([...expectedHeadFiles]).some((filePath) => !analyzedHeadFiles.has(filePath))) return null;
2201
+ const baselineLintPaths = new Set([...baselineIncludePaths, ...filteredSnapshotBaseFiles].map(toForwardSlashes));
2202
+ const materializedLintPaths = snapshot.materializedFiles.filter((filePath) => baselineLintPaths.has(toForwardSlashes(filePath)));
2203
+ const maintainabilityFocusPaths = [...new Set([...input.options.includePaths, ...snapshot.baseFiles].map(toForwardSlashes))];
2204
+ const baselineExcludedProjectDirectories = input.options.excludedProjectDirectories.map((excludedDirectory) => path.resolve(excludedDirectory)).filter((excludedDirectory) => isPathInsideDirectory(excludedDirectory, input.directory)).map((excludedDirectory) => path.resolve(snapshot.tempDirectory, path.relative(input.directory, excludedDirectory)));
2205
+ const baseIncludePaths = materializedLintPaths.length > 0 ? materializedLintPaths : maintainabilityFocusPaths;
2147
2206
  const runtimeLayers = buildRuntimeLayers({
2148
2207
  directory: snapshot.tempDirectory,
2149
2208
  hasConfigOverride: true,
2150
2209
  userConfig: input.userConfig,
2151
2210
  configSourceDirectory: input.configSourceDirectory,
2152
2211
  projectInfoOverride: input.headProjectInfo,
2153
- shouldSkipLint: !input.options.lint || !input.resolvedNodeBinaryPath,
2154
- shouldRunDeadCode: false,
2212
+ shouldSkipLint: !input.options.lint || !input.resolvedNodeBinaryPath || materializedLintPaths.length === 0,
2213
+ shouldRunDeadCode: input.options.deadCode,
2155
2214
  shouldRunSupplyChain: input.options.supplyChain,
2156
2215
  shouldComputeScore: false,
2157
2216
  shouldShowProgressSpinners: false,
@@ -2160,7 +2219,8 @@ const runBaselineComparison = async (input) => {
2160
2219
  });
2161
2220
  const baseOutput = await runPromise(restoreLegacyThrow(runInspect({
2162
2221
  directory: snapshot.tempDirectory,
2163
- includePaths: snapshot.materializedFiles,
2222
+ includePaths: baseIncludePaths,
2223
+ maintainabilityFocusPaths,
2164
2224
  customRulesOnly: input.options.customRulesOnly,
2165
2225
  respectInlineDisables: input.options.respectInlineDisables,
2166
2226
  warnings: input.options.warnings,
@@ -2169,7 +2229,7 @@ const runBaselineComparison = async (input) => {
2169
2229
  includedTags: input.options.includedTags,
2170
2230
  includeTagDefaults: input.options.includeTagDefaults,
2171
2231
  nodeBinaryPath: input.resolvedNodeBinaryPath ?? void 0,
2172
- runDeadCode: false,
2232
+ runDeadCode: input.options.deadCode,
2173
2233
  isCi: input.options.isCi,
2174
2234
  doctorVersion: VERSION,
2175
2235
  runId: getRunId(),
@@ -2177,10 +2237,16 @@ const runBaselineComparison = async (input) => {
2177
2237
  suppressScanSummary: true,
2178
2238
  supplyChainManifestChanged: input.options.supplyChainManifestChanged,
2179
2239
  deadlineEpochMs: input.deadlineEpochMs ?? void 0,
2180
- signal: input.oxlintRuntime.abortSignal
2240
+ signal: input.oxlintRuntime.abortSignal,
2241
+ excludedProjectDirectories: baselineExcludedProjectDirectories,
2242
+ retainExcludedProjectDeadCodeDiagnostics: input.options.retainExcludedProjectDeadCodeDiagnostics
2181
2243
  }, {}).pipe(provide(runtimeLayers), provideService(PerFileLintCacheEnabled, false), provideService(SidecarLintCacheEnabled, false), provideService(Console, silentConsole$1))), { signal: input.oxlintRuntime.abortSignal });
2182
- if (baseOutput.didLintFail || countIncompleteLintFiles(baseOutput.lintPartialFailures) > 0) return null;
2183
- const hasUnscannedUntrackedSourceFiles = filterSourceFiles(snapshot.untrackedFiles.map(toForwardSlashes)).some((filePath) => !analyzedHeadFiles.has(filePath));
2244
+ if (baseOutput.didLintFail || baseOutput.didDeadCodeFail || countIncompleteLintFiles(baseOutput.lintPartialFailures) > 0) return null;
2245
+ const hasUnscannedUntrackedSourceFiles = filterSourceFiles(filterPathsOutsideDirectories({
2246
+ rootDirectory: input.directory,
2247
+ relativePaths: snapshot.untrackedFiles,
2248
+ excludedDirectories: input.options.excludedProjectDirectories
2249
+ }).map(toForwardSlashes)).some((filePath) => !analyzedHeadFiles.has(filePath));
2184
2250
  const diagnosticDelta = computeDiagnosticDelta({
2185
2251
  headDiagnostics: input.headDiagnostics,
2186
2252
  baseDiagnostics: baseOutput.diagnostics,
@@ -2353,6 +2419,7 @@ const runInspectWithRuntime = async (directory, options, userConfig, hasConfigOv
2353
2419
  directory,
2354
2420
  precomputedSourceFileCount: options.precomputedSourceFileCount,
2355
2421
  includePaths: options.includePaths,
2422
+ changedLineRanges: options.changedLineRanges ?? void 0,
2356
2423
  customRulesOnly: options.customRulesOnly,
2357
2424
  respectInlineDisables: options.respectInlineDisables,
2358
2425
  warnings: options.warnings,
@@ -2383,7 +2450,7 @@ const runInspectWithRuntime = async (directory, options, userConfig, hasConfigOv
2383
2450
  else runSync(error(highlighter.error(lintFailureReason)));
2384
2451
  let inspectDiagnostics = output.diagnostics;
2385
2452
  let baselineDelta;
2386
- if (options.baseline && isDiffMode && !didLintFail && countIncompleteLintFiles(output.lintPartialFailures) === 0) {
2453
+ if (options.baseline && isDiffMode && !didLintFail && !output.didDeadCodeFail && countIncompleteLintFiles(output.lintPartialFailures) === 0) {
2387
2454
  const comparison = await runBaselineComparison({
2388
2455
  directory,
2389
2456
  options,
@@ -2536,13 +2603,13 @@ const resolveCursorBundledNode = () => {
2536
2603
  if (!localAppData) return null;
2537
2604
  const cursorAgentRoot = path$1.join(localAppData, "cursor-agent", "versions");
2538
2605
  try {
2539
- const versions = fs$1.readdirSync(cursorAgentRoot);
2606
+ const versions = fs$2.readdirSync(cursorAgentRoot);
2540
2607
  if (versions.length === 0) return null;
2541
2608
  const sortedVersions = versions.sort((a, b) => b.localeCompare(a, void 0, { numeric: true }));
2542
2609
  for (const version of sortedVersions) try {
2543
2610
  const bundledNodePath = path$1.join(cursorAgentRoot, version, "node.exe");
2544
2611
  const entryScriptPath = path$1.join(cursorAgentRoot, version, "index.js");
2545
- if (fs$1.statSync(bundledNodePath).isFile() && fs$1.statSync(entryScriptPath).isFile()) return {
2612
+ if (fs$2.statSync(bundledNodePath).isFile() && fs$2.statSync(entryScriptPath).isFile()) return {
2546
2613
  command: bundledNodePath,
2547
2614
  args: [entryScriptPath]
2548
2615
  };
@@ -2556,15 +2623,15 @@ const resolveWindowsCmdEntryScript = (command) => {
2556
2623
  for (const directory of pathDirectories) {
2557
2624
  const cmdFilePath = path$1.join(directory, `${command}.cmd`);
2558
2625
  try {
2559
- if (!fs$1.statSync(cmdFilePath).isFile()) continue;
2626
+ if (!fs$2.statSync(cmdFilePath).isFile()) continue;
2560
2627
  } catch {
2561
2628
  continue;
2562
2629
  }
2563
2630
  try {
2564
- const entryScriptMatch = fs$1.readFileSync(cmdFilePath, "utf8").match(/"%(?:~dp0|dp0%)\\([^"]+\.(?:m?js|cjs))"/);
2631
+ const entryScriptMatch = fs$2.readFileSync(cmdFilePath, "utf8").match(/"%(?:~dp0|dp0%)\\([^"]+\.(?:m?js|cjs))"/);
2565
2632
  if (!entryScriptMatch) continue;
2566
2633
  const resolvedScriptPath = path$1.resolve(directory, entryScriptMatch[1]);
2567
- if (fs$1.statSync(resolvedScriptPath).isFile()) return resolvedScriptPath;
2634
+ if (fs$2.statSync(resolvedScriptPath).isFile()) return resolvedScriptPath;
2568
2635
  } catch {}
2569
2636
  }
2570
2637
  return null;
@@ -2829,6 +2896,45 @@ const resolveProjectScan = async (rootScanTarget, projectDirectory) => {
2829
2896
  };
2830
2897
  };
2831
2898
  //#endregion
2899
+ //#region src/cli/utils/highlight-code-line.ts
2900
+ const CODE_TOKEN_PATTERN = /\/\/.*|\/\*.*?\*\/|"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|`(?:\\.|[^`\\])*`|\b(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|export|extends|false|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|true|try|type|typeof|undefined|var|void|while|with|yield)\b|\b(?:0[xob][\da-f]+|\d+(?:\.\d+)?)\b/giu;
2901
+ const highlightCodeLine = (codeLine) => codeLine.replace(CODE_TOKEN_PATTERN, (token) => {
2902
+ if (token.startsWith("/")) return highlighter.gray(token);
2903
+ if (token.startsWith("\"") || token.startsWith("'") || token.startsWith("`")) return highlighter.success(token);
2904
+ if (/^\d|^0[xob]/i.test(token)) return highlighter.warn(token);
2905
+ return highlighter.info(token);
2906
+ });
2907
+ //#endregion
2908
+ //#region src/cli/utils/render-code-frame.ts
2909
+ const SOURCE_LINE_BREAK_PATTERN = /\r\n|[\n\r\u2028\u2029]/;
2910
+ const renderCodeFrame = ({ source, line, column, endLine, message, linesAbove, linesBelow, maximumLineLength }) => {
2911
+ const sourceLines = source.split(SOURCE_LINE_BREAK_PATTERN);
2912
+ if (line < 1 || line > sourceLines.length) return null;
2913
+ const offendingLine = sourceLines[line - 1];
2914
+ if (offendingLine === void 0 || offendingLine.length > maximumLineLength) return null;
2915
+ const lastMarkedLine = Math.min(Math.max(endLine ?? line, line), sourceLines.length);
2916
+ const firstDisplayedLine = Math.max(line - linesAbove, 1);
2917
+ const lastDisplayedLine = Math.min(lastMarkedLine + linesBelow, sourceLines.length);
2918
+ const lineNumberWidth = String(lastDisplayedLine).length;
2919
+ const hasCaret = lastMarkedLine === line && column !== void 0;
2920
+ const outputLines = [];
2921
+ if (message && !hasCaret) outputLines.push(`${" ".repeat(lineNumberWidth + 2)}${highlighter.error(message)}`);
2922
+ for (let displayedLineNumber = firstDisplayedLine; displayedLineNumber <= lastDisplayedLine; displayedLineNumber += 1) {
2923
+ const sourceLine = sourceLines[displayedLineNumber - 1] ?? "";
2924
+ const isMarked = displayedLineNumber >= line && displayedLineNumber <= lastMarkedLine;
2925
+ const gutter = ` ${String(displayedLineNumber).padStart(lineNumberWidth)} |`;
2926
+ const marker = isMarked ? highlighter.error(">") : " ";
2927
+ outputLines.push(`${marker}${highlighter.dim(gutter)}${sourceLine.length > 0 ? ` ${highlightCodeLine(sourceLine)}` : ""}`);
2928
+ if (isMarked && hasCaret) {
2929
+ const markerSpacing = sourceLine.slice(0, Math.max(column - 1, 0)).replace(/[^\t]/g, " ");
2930
+ const emptyGutter = gutter.replace(/\d/g, " ");
2931
+ const markerMessage = message ? ` ${highlighter.error(message)}` : "";
2932
+ outputLines.push(` ${highlighter.dim(emptyGutter)} ${markerSpacing}${highlighter.error("^")}${markerMessage}`);
2933
+ }
2934
+ }
2935
+ return outputLines.join("\n");
2936
+ };
2937
+ //#endregion
2832
2938
  //#region src/cli/utils/resolve-absolute-path.ts
2833
2939
  /**
2834
2940
  * Resolves a diagnostic's `filePath` (relative to its project root, or
@@ -2851,23 +2957,20 @@ const buildCodeFrame = (input) => {
2851
2957
  const absolutePath = resolveAbsolutePath(input.filePath, input.rootDirectory);
2852
2958
  let source;
2853
2959
  try {
2854
- source = fs$1.readFileSync(absolutePath, "utf8");
2960
+ source = fs$2.readFileSync(absolutePath, "utf8");
2855
2961
  } catch {
2856
2962
  return null;
2857
2963
  }
2858
- if ((source.split("\n", input.line)[input.line - 1] ?? "").length > 200) return null;
2859
- const location = input.endLine != null && input.endLine > input.line ? {
2860
- start: { line: input.line },
2861
- end: { line: input.endLine }
2862
- } : { start: {
2964
+ const endLine = input.endLine != null && input.endLine > input.line ? input.endLine : void 0;
2965
+ return renderCodeFrame({
2966
+ source,
2863
2967
  line: input.line,
2864
- column: input.column > 0 ? input.column : void 0
2865
- } };
2866
- return codeFrameColumns(source, location, {
2867
- highlightCode: true,
2968
+ column: endLine === void 0 && input.column > 0 ? input.column : void 0,
2969
+ endLine,
2970
+ message: input.message,
2868
2971
  linesAbove: 1,
2869
2972
  linesBelow: 1,
2870
- ...input.message ? { message: input.message } : {}
2973
+ maximumLineLength: 200
2871
2974
  });
2872
2975
  };
2873
2976
  //#endregion
@@ -2875,7 +2978,7 @@ const buildCodeFrame = (input) => {
2875
2978
  const discoverWorkspacePackages = (rootDirectory) => {
2876
2979
  const hasRootPackageJson = isFile(path$1.join(rootDirectory, "package.json"));
2877
2980
  const packages = listWorkspacePackages(rootDirectory);
2878
- if (packages.length === 0 && (!hasRootPackageJson || isMonorepoRoot(rootDirectory))) return discoverReactSubprojects(rootDirectory);
2981
+ if (packages.length === 0 && (!hasRootPackageJson || isMonorepoRoot(rootDirectory))) return discoverSupportedSubprojects(rootDirectory);
2879
2982
  return packages;
2880
2983
  };
2881
2984
  const selectProjects = async (rootDirectory, projectFlag, skipPrompts, configProjects) => {
@@ -2991,7 +3094,7 @@ const shouldFailScanGate = (input) => {
2991
3094
  return shouldBlockCi(filterScansForSurface(input.scans, "ciFailure"), input.blockingLevel);
2992
3095
  };
2993
3096
  //#endregion
2994
- export { resolveMergeBaseRef as A, buildEmptyReportMessage as B, copyToClipboard as C, deduplicateProjectScans as D, collectProjectSourceFileCounts as E, printDiagnosticsDump as F, formatLearnMoreLine as G, buildRulePriorityMap as H, formatDiagnosticSite as I, hasIncompleteScoreAnalysis as L, pluralize as M, formatElapsedTime as N, createInvocationInspect as O, printFooter as P, filterDiagnosticsByCategories as R, CLI_AGENT_BINARIES as S, buildHandoffPayload as T, buildSortedRuleGroups as U, hasLintHardFailure as V, formatFixRecipeLine as W, resolveCliInspectOptions as _, projectManifestChanged as a, setUpGitHubActions as b, selectProjects as c, resolveProjectChangedLineRanges as d, resolveProjectDiffIncludePaths as f, resolveWorkspaceDeadCodeOwner as g, retryMissingProjectScores as h, validateModeFlags as i, printHeadlessReport as j, inspect as k, buildCodeFrame as l, resolveProjectRelativeDirectory as m, formatSkippedProjectsMessage as n, partitionProjectScanOutcomes as o, resolveProjectSourceFilePaths as p, validateIncludeUntrackedScope as r, discoverWorkspacePackages as s, shouldFailScanGate as t, resolveProjectScan as u, filterScansForSurface as v, launchCliAgent as w, detectLaunchableAgents as x, countUniqueScannedFiles as y, buildNoScoreMessage as z };
3097
+ export { resolveMergeBaseRef as A, buildNoScoreMessage as B, copyToClipboard as C, deduplicateProjectScans as D, collectProjectSourceFileCounts as E, printFooter as F, formatFixRecipeLine as G, hasLintHardFailure as H, printDiagnosticsDump as I, formatLearnMoreLine as K, formatDiagnosticSite as L, pluralize as M, formatSkippedCheckLabel as N, createInvocationInspect as O, formatElapsedTime as P, hasIncompleteScoreAnalysis as R, CLI_AGENT_BINARIES as S, buildHandoffPayload as T, buildRulePriorityMap as U, buildEmptyReportMessage as V, buildSortedRuleGroups as W, resolveCliInspectOptions as _, projectManifestChanged as a, setUpGitHubActions as b, selectProjects as c, resolveProjectChangedLineRanges as d, resolveProjectDiffIncludePaths as f, resolveWorkspaceDeadCodeOwner as g, retryMissingProjectScores as h, validateModeFlags as i, printHeadlessReport as j, inspect as k, buildCodeFrame as l, resolveProjectRelativeDirectory as m, formatSkippedProjectsMessage as n, partitionProjectScanOutcomes as o, resolveProjectSourceFilePaths as p, validateIncludeUntrackedScope as r, discoverWorkspacePackages as s, shouldFailScanGate as t, resolveProjectScan as u, filterScansForSurface as v, launchCliAgent as w, detectLaunchableAgents as x, countUniqueScannedFiles as y, filterDiagnosticsByCategories as z };
2995
3098
 
2996
- //# sourceMappingURL=should-fail-scan-gate-4jf7ZRdL.js.map
2997
- //# debugId=1be0ae40-8cad-55a2-b447-69faa6b7cb0b
3099
+ //# sourceMappingURL=should-fail-scan-gate-Dch9vbPz.js.map
3100
+ //# debugId=19bff337-5a1f-5558-b6b7-bd4274f53f38