UNPKG

pi-lens

Version:

Real-time code feedback for pi — LSP, linters, formatters, type-checking, structural analysis & booboo

2,335 lines • 60.6 kB
import { createRequire as __pilensCreateRequire } from "node:module"; const require = __pilensCreateRequire(import.meta.url);
import {
  resolvePackagePath
} from "./chunk-QOZXJ4NA.js";
import {
  loadProjectSnapshotWithoutWordIndex
} from "./chunk-AJVLWNFN.js";
import {
  TERRAGRUNT_FILENAMES
} from "./chunk-5ZEJHV35.js";
import {
  recordDegradationOnce
} from "./chunk-N3YQJI6O.js";
import {
  logLatency
} from "./chunk-5THXD6X5.js";
import {
  findNearestContaining,
  normalizeMapKey,
  walkUpDirs
} from "./chunk-Q5U6FMDI.js";

// dist/clients/tool-policy.js
import * as fs from "node:fs";
import * as path from "node:path";
var FORMATTER_POLICY_BY_EXTENSION = /* @__PURE__ */ new Map([
  [
    ".js",
    {
      formatterNames: ["biome", "prettier", "oxfmt"],
      defaultFormatter: "biome",
      defaultWhenUnconfigured: true,
      gate: "smart-default"
    }
  ],
  [
    ".jsx",
    {
      formatterNames: ["biome", "prettier", "oxfmt"],
      defaultFormatter: "biome",
      defaultWhenUnconfigured: true,
      gate: "smart-default"
    }
  ],
  [
    ".mjs",
    {
      formatterNames: ["biome", "prettier", "oxfmt"],
      defaultFormatter: "biome",
      defaultWhenUnconfigured: true,
      gate: "smart-default"
    }
  ],
  [
    ".cjs",
    {
      formatterNames: ["biome", "prettier", "oxfmt"],
      defaultFormatter: "biome",
      defaultWhenUnconfigured: true,
      gate: "smart-default"
    }
  ],
  [
    ".ts",
    {
      formatterNames: ["biome", "prettier", "oxfmt"],
      defaultFormatter: "biome",
      defaultWhenUnconfigured: true,
      gate: "smart-default"
    }
  ],
  [
    ".tsx",
    {
      formatterNames: ["biome", "prettier", "oxfmt"],
      defaultFormatter: "biome",
      defaultWhenUnconfigured: true,
      gate: "smart-default"
    }
  ],
  [
    ".mts",
    {
      formatterNames: ["biome", "prettier", "oxfmt"],
      defaultFormatter: "biome",
      defaultWhenUnconfigured: true,
      gate: "smart-default"
    }
  ],
  [
    ".cts",
    {
      formatterNames: ["biome", "prettier", "oxfmt"],
      defaultFormatter: "biome",
      defaultWhenUnconfigured: true,
      gate: "smart-default"
    }
  ],
  [
    ".py",
    {
      formatterNames: ["black", "ruff"],
      defaultFormatter: "ruff",
      defaultWhenUnconfigured: true,
      gate: "smart-default"
    }
  ],
  [
    ".pyi",
    {
      formatterNames: ["black", "ruff"],
      defaultFormatter: "ruff",
      defaultWhenUnconfigured: true,
      gate: "smart-default"
    }
  ],
  [
    ".json",
    {
      formatterNames: ["biome", "prettier"],
      defaultFormatter: "biome",
      defaultWhenUnconfigured: false,
      gate: "mixed"
    }
  ],
  [
    ".jsonc",
    {
      formatterNames: ["biome", "prettier"],
      defaultFormatter: "biome",
      defaultWhenUnconfigured: false,
      gate: "mixed"
    }
  ],
  [
    ".css",
    {
      formatterNames: ["biome", "prettier", "oxfmt"],
      defaultFormatter: "biome",
      defaultWhenUnconfigured: true,
      gate: "smart-default"
    }
  ],
  [
    ".scss",
    {
      formatterNames: ["biome", "prettier", "oxfmt"],
      defaultFormatter: "biome",
      defaultWhenUnconfigured: true,
      gate: "smart-default"
    }
  ],
  [
    ".sass",
    {
      // #1135: no oxfmt (see map header comment).
      formatterNames: ["biome", "prettier"],
      defaultFormatter: "biome",
      defaultWhenUnconfigured: true,
      gate: "smart-default"
    }
  ],
  [
    ".less",
    {
      formatterNames: ["prettier"],
      defaultFormatter: "prettier",
      defaultWhenUnconfigured: true,
      gate: "smart-default"
    }
  ],
  [
    ".html",
    {
      formatterNames: ["prettier"],
      defaultFormatter: "prettier",
      // HTML commonly carries template markers ({{JS}} embeds, Handlebars,
      // Helm-adjacent Go templates). Prettier reinterprets them as code and
      // corrupts them. Opt in via project prettier config; do not run by default.
      defaultWhenUnconfigured: false,
      gate: "smart-default"
    }
  ],
  [
    ".htm",
    {
      formatterNames: ["prettier"],
      defaultFormatter: "prettier",
      defaultWhenUnconfigured: false,
      gate: "smart-default"
    }
  ],
  [
    ".yaml",
    {
      formatterNames: ["prettier"],
      defaultFormatter: "prettier",
      // YAML commonly carries template markers (Helm `{{ .Values.x }}`,
      // Ansible `{{ var }}`). Prettier splits `{{` into `{ {` and reindents
      // embedded templates. Opt in via project prettier config.
      defaultWhenUnconfigured: false,
      gate: "smart-default"
    }
  ],
  [
    ".yml",
    {
      formatterNames: ["prettier"],
      defaultFormatter: "prettier",
      defaultWhenUnconfigured: false,
      gate: "smart-default"
    }
  ],
  [
    ".md",
    {
      formatterNames: ["prettier"],
      defaultFormatter: "prettier",
      // Prettier's markdown defaults reflow lines, normalize emphasis (* -> _),
      // and restyle lists. Opt-in via project prettier config; do not run by default.
      defaultWhenUnconfigured: false,
      gate: "smart-default"
    }
  ],
  [
    ".mdx",
    {
      formatterNames: ["prettier"],
      defaultFormatter: "prettier",
      defaultWhenUnconfigured: false,
      gate: "smart-default"
    }
  ],
  [
    ".graphql",
    {
      formatterNames: ["prettier"],
      defaultFormatter: "prettier",
      defaultWhenUnconfigured: true,
      gate: "smart-default"
    }
  ],
  [
    ".gql",
    {
      formatterNames: ["prettier"],
      defaultFormatter: "prettier",
      defaultWhenUnconfigured: true,
      gate: "smart-default"
    }
  ],
  [
    // ktfmt is offered alongside ktlint but only wins when the project opts in
    // (hasExplicitFormatterConfig("ktfmt")); otherwise ktlint stays the
    // smart-default. #129
    ".kt",
    {
      formatterNames: ["ktfmt", "ktlint"],
      defaultFormatter: "ktlint",
      defaultWhenUnconfigured: false,
      gate: "smart-default"
    }
  ],
  [
    ".kts",
    {
      formatterNames: ["ktfmt", "ktlint"],
      defaultFormatter: "ktlint",
      defaultWhenUnconfigured: false,
      gate: "smart-default"
    }
  ],
  [
    ".swift",
    {
      formatterNames: ["swiftformat"],
      defaultFormatter: "swiftformat",
      defaultWhenUnconfigured: false,
      gate: "config-first"
    }
  ],
  [
    ".fs",
    {
      formatterNames: ["fantomas"],
      defaultFormatter: "fantomas",
      defaultWhenUnconfigured: false,
      gate: "config-first"
    }
  ],
  [
    ".fsi",
    {
      formatterNames: ["fantomas"],
      defaultFormatter: "fantomas",
      defaultWhenUnconfigured: false,
      gate: "config-first"
    }
  ],
  [
    ".fsx",
    {
      formatterNames: ["fantomas"],
      defaultFormatter: "fantomas",
      defaultWhenUnconfigured: false,
      gate: "config-first"
    }
  ],
  [
    ".nix",
    {
      formatterNames: ["nixfmt"],
      defaultFormatter: "nixfmt",
      defaultWhenUnconfigured: false,
      gate: "smart-default"
    }
  ],
  [
    ".ex",
    {
      formatterNames: ["mix"],
      defaultFormatter: "mix",
      defaultWhenUnconfigured: false,
      gate: "config-first"
    }
  ],
  [
    ".exs",
    {
      formatterNames: ["mix"],
      defaultFormatter: "mix",
      defaultWhenUnconfigured: false,
      gate: "config-first"
    }
  ],
  [
    ".eex",
    {
      formatterNames: ["mix"],
      defaultFormatter: "mix",
      defaultWhenUnconfigured: false,
      gate: "config-first"
    }
  ],
  [
    ".heex",
    {
      formatterNames: ["mix"],
      defaultFormatter: "mix",
      defaultWhenUnconfigured: false,
      gate: "config-first"
    }
  ],
  [
    ".leex",
    {
      formatterNames: ["mix"],
      defaultFormatter: "mix",
      defaultWhenUnconfigured: false,
      gate: "config-first"
    }
  ],
  [
    ".gleam",
    {
      formatterNames: ["gleam"],
      defaultFormatter: "gleam",
      defaultWhenUnconfigured: true,
      gate: "smart-default"
    }
  ],
  [
    ".typ",
    {
      formatterNames: ["typstyle"],
      defaultFormatter: "typstyle",
      defaultWhenUnconfigured: true,
      gate: "smart-default"
    }
  ],
  [
    ".typc",
    {
      formatterNames: ["typstyle"],
      defaultFormatter: "typstyle",
      defaultWhenUnconfigured: true,
      gate: "smart-default"
    }
  ],
  [
    ".c",
    {
      formatterNames: ["clang-format"],
      defaultFormatter: "clang-format",
      defaultWhenUnconfigured: false,
      gate: "config-first"
    }
  ],
  [
    ".cc",
    {
      formatterNames: ["clang-format"],
      defaultFormatter: "clang-format",
      defaultWhenUnconfigured: false,
      gate: "config-first"
    }
  ],
  [
    ".cpp",
    {
      formatterNames: ["clang-format"],
      defaultFormatter: "clang-format",
      defaultWhenUnconfigured: false,
      gate: "config-first"
    }
  ],
  [
    ".cxx",
    {
      formatterNames: ["clang-format"],
      defaultFormatter: "clang-format",
      defaultWhenUnconfigured: false,
      gate: "config-first"
    }
  ],
  [
    ".h",
    {
      formatterNames: ["clang-format"],
      defaultFormatter: "clang-format",
      defaultWhenUnconfigured: false,
      gate: "config-first"
    }
  ],
  [
    ".hpp",
    {
      formatterNames: ["clang-format"],
      defaultFormatter: "clang-format",
      defaultWhenUnconfigured: false,
      gate: "config-first"
    }
  ],
  [
    ".ino",
    {
      formatterNames: ["clang-format"],
      defaultFormatter: "clang-format",
      defaultWhenUnconfigured: false,
      gate: "config-first"
    }
  ],
  [
    ".php",
    {
      formatterNames: ["php-cs-fixer"],
      defaultFormatter: "php-cs-fixer",
      defaultWhenUnconfigured: false,
      gate: "config-first"
    }
  ],
  [
    ".cs",
    {
      formatterNames: ["csharpier"],
      defaultFormatter: "csharpier",
      defaultWhenUnconfigured: false,
      gate: "config-first"
    }
  ],
  [
    ".lua",
    {
      formatterNames: ["stylua"],
      defaultFormatter: "stylua",
      defaultWhenUnconfigured: false,
      gate: "config-first"
    }
  ],
  [
    ".hs",
    {
      formatterNames: ["ormolu"],
      defaultFormatter: "ormolu",
      defaultWhenUnconfigured: false,
      gate: "config-first"
    }
  ],
  [
    ".lhs",
    {
      formatterNames: ["ormolu"],
      defaultFormatter: "ormolu",
      defaultWhenUnconfigured: false,
      gate: "config-first"
    }
  ],
  [
    ".ml",
    {
      formatterNames: ["ocamlformat"],
      defaultFormatter: "ocamlformat",
      defaultWhenUnconfigured: false,
      gate: "config-first"
    }
  ],
  [
    ".mli",
    {
      formatterNames: ["ocamlformat"],
      defaultFormatter: "ocamlformat",
      defaultWhenUnconfigured: false,
      gate: "config-first"
    }
  ],
  [
    ".go",
    {
      formatterNames: ["gofmt"],
      defaultFormatter: "gofmt",
      defaultWhenUnconfigured: true,
      gate: "smart-default"
    }
  ],
  [
    ".rs",
    {
      formatterNames: ["rustfmt"],
      defaultFormatter: "rustfmt",
      defaultWhenUnconfigured: true,
      gate: "smart-default"
    }
  ],
  [
    ".sh",
    {
      formatterNames: ["shfmt"],
      defaultFormatter: "shfmt",
      defaultWhenUnconfigured: true,
      gate: "smart-default"
    }
  ],
  [
    ".bash",
    {
      formatterNames: ["shfmt"],
      defaultFormatter: "shfmt",
      defaultWhenUnconfigured: true,
      gate: "smart-default"
    }
  ],
  [
    ".toml",
    {
      formatterNames: ["taplo"],
      defaultFormatter: "taplo",
      defaultWhenUnconfigured: false,
      gate: "config-first"
    }
  ],
  [
    ".tf",
    {
      formatterNames: ["terraform"],
      defaultFormatter: "terraform",
      defaultWhenUnconfigured: false,
      gate: "config-first"
    }
  ],
  [
    ".tfvars",
    {
      formatterNames: ["terraform"],
      defaultFormatter: "terraform",
      defaultWhenUnconfigured: false,
      gate: "config-first"
    }
  ],
  [
    ".dart",
    {
      formatterNames: ["dart"],
      defaultFormatter: "dart",
      defaultWhenUnconfigured: true,
      gate: "smart-default"
    }
  ],
  [
    ".zig",
    {
      formatterNames: ["zig"],
      defaultFormatter: "zig",
      defaultWhenUnconfigured: true,
      gate: "smart-default"
    }
  ],
  [
    ".zon",
    {
      formatterNames: ["zig"],
      defaultFormatter: "zig",
      defaultWhenUnconfigured: true,
      gate: "smart-default"
    }
  ],
  [
    ".java",
    {
      formatterNames: ["google-java-format"],
      defaultFormatter: "google-java-format",
      defaultWhenUnconfigured: false,
      gate: "config-first"
    }
  ],
  [
    ".clj",
    {
      formatterNames: ["cljfmt"],
      defaultFormatter: "cljfmt",
      defaultWhenUnconfigured: false,
      gate: "config-first"
    }
  ],
  [
    ".cljc",
    {
      formatterNames: ["cljfmt"],
      defaultFormatter: "cljfmt",
      defaultWhenUnconfigured: false,
      gate: "config-first"
    }
  ],
  [
    ".cljs",
    {
      formatterNames: ["cljfmt"],
      defaultFormatter: "cljfmt",
      defaultWhenUnconfigured: false,
      gate: "config-first"
    }
  ],
  [
    ".cmake",
    {
      formatterNames: ["cmake-format"],
      defaultFormatter: "cmake-format",
      defaultWhenUnconfigured: false,
      gate: "config-first"
    }
  ],
  [
    ".ps1",
    {
      formatterNames: ["psscriptanalyzer-format"],
      defaultFormatter: "psscriptanalyzer-format",
      defaultWhenUnconfigured: false,
      gate: "smart-default"
    }
  ],
  [
    ".psm1",
    {
      formatterNames: ["psscriptanalyzer-format"],
      defaultFormatter: "psscriptanalyzer-format",
      defaultWhenUnconfigured: false,
      gate: "smart-default"
    }
  ],
  [
    ".psd1",
    {
      formatterNames: ["psscriptanalyzer-format"],
      defaultFormatter: "psscriptanalyzer-format",
      defaultWhenUnconfigured: false,
      gate: "smart-default"
    }
  ],
  [
    ".cue",
    {
      formatterNames: ["cue"],
      defaultFormatter: "cue",
      defaultWhenUnconfigured: true,
      gate: "smart-default"
    }
  ]
]);
var OXFMT_SUPPORTED_EXTENSIONS = /* @__PURE__ */ new Set([
  ".js",
  ".jsx",
  ".mjs",
  ".cjs",
  ".ts",
  ".tsx",
  ".mts",
  ".cts",
  ".vue",
  ".svelte",
  ".css",
  ".scss",
  ".less",
  ".html",
  ".htm",
  ".json",
  ".jsonc",
  ".yaml",
  ".yml",
  ".md",
  ".mdx",
  ".graphql",
  ".gql",
  ".toml"
]);
FORMATTER_POLICY_BY_EXTENSION.set(".vue", {
  formatterNames: ["prettier"],
  defaultFormatter: "prettier",
  defaultWhenUnconfigured: false,
  gate: "config-first"
});
FORMATTER_POLICY_BY_EXTENSION.set(".svelte", {
  formatterNames: [],
  defaultFormatter: "oxfmt",
  defaultWhenUnconfigured: false,
  gate: "config-first"
});
for (const [ext, policy] of FORMATTER_POLICY_BY_EXTENSION) {
  if (OXFMT_SUPPORTED_EXTENSIONS.has(ext) && !policy.formatterNames.includes("oxfmt")) {
    policy.formatterNames.push("oxfmt");
  }
}
var AUTO_INSTALLABLE_DEFAULT_FORMATTERS = /* @__PURE__ */ new Map([
  ["biome", "biome"],
  ["ruff", "ruff"],
  ["prettier", "prettier"],
  ["shfmt", "shfmt"],
  ["taplo", "taplo"],
  ["ktlint", "ktlint"],
  ["typstyle", "typstyle"]
]);
var TERRAGRUNT_FORMATTER_POLICY = {
  formatterNames: ["terragrunt-hcl"],
  defaultFormatter: "terragrunt-hcl",
  defaultWhenUnconfigured: true,
  gate: "smart-default"
};
var FORMATTER_POLICY_BY_FILENAME = new Map(TERRAGRUNT_FILENAMES.map((name) => [name, TERRAGRUNT_FORMATTER_POLICY]));
function getFormatterPolicyForExtension(ext) {
  return FORMATTER_POLICY_BY_EXTENSION.get(ext.toLowerCase());
}
function getFormatterPolicyForFile(filePath) {
  const byFilename = FORMATTER_POLICY_BY_FILENAME.get(path.basename(filePath).toLowerCase());
  if (byFilename)
    return byFilename;
  return getFormatterPolicyForExtension(path.extname(filePath));
}
function getSmartDefaultFormatterName(filePath) {
  const policy = getFormatterPolicyForFile(filePath);
  if (!policy?.defaultWhenUnconfigured)
    return void 0;
  return policy.defaultFormatter;
}
function getAutoInstallToolIdForFormatter(formatterName) {
  return AUTO_INSTALLABLE_DEFAULT_FORMATTERS.get(formatterName);
}
function getToolExecutionPolicy(toolId) {
  return TOOL_EXECUTION_POLICY.get(toolId);
}
function shouldAutoInstallTool(toolId) {
  return getToolExecutionPolicy(toolId)?.autoInstall ?? false;
}
function getAutofixCapability(toolId) {
  return AUTOFIX_CAPABILITIES.get(toolId);
}
function getToolCommandSpec(toolId) {
  return TOOL_COMMAND_SPECS.get(toolId);
}
var AUTOFIX_CAPABILITIES = /* @__PURE__ */ new Map([
  [
    "biome",
    { toolSupportsFix: true, safePipelineAutofix: true, fixKind: "pipeline" }
  ],
  [
    "eslint",
    { toolSupportsFix: true, safePipelineAutofix: true, fixKind: "pipeline" }
  ],
  [
    "ruff",
    { toolSupportsFix: true, safePipelineAutofix: true, fixKind: "pipeline" }
  ],
  [
    "stylelint",
    { toolSupportsFix: true, safePipelineAutofix: true, fixKind: "pipeline" }
  ],
  [
    "sqlfluff",
    { toolSupportsFix: true, safePipelineAutofix: true, fixKind: "pipeline" }
  ],
  [
    "rubocop",
    { toolSupportsFix: true, safePipelineAutofix: true, fixKind: "pipeline" }
  ],
  [
    "ktlint",
    { toolSupportsFix: true, safePipelineAutofix: true, fixKind: "pipeline" }
  ],
  [
    "ktfmt",
    { toolSupportsFix: true, safePipelineAutofix: true, fixKind: "pipeline" }
  ],
  [
    "rust-clippy",
    { toolSupportsFix: true, safePipelineAutofix: true, fixKind: "pipeline" }
  ],
  [
    "dart-analyze",
    { toolSupportsFix: true, safePipelineAutofix: true, fixKind: "pipeline" }
  ],
  [
    "golangci-lint",
    { toolSupportsFix: true, safePipelineAutofix: true, fixKind: "pipeline" }
  ],
  [
    "detekt",
    { toolSupportsFix: true, safePipelineAutofix: true, fixKind: "pipeline" }
  ],
  [
    "markdownlint",
    { toolSupportsFix: true, safePipelineAutofix: true, fixKind: "pipeline" }
  ],
  [
    "oxlint",
    { toolSupportsFix: true, safePipelineAutofix: true, fixKind: "pipeline" }
  ]
]);
var TOOL_EXECUTION_POLICY = /* @__PURE__ */ new Map([
  // Dispatch/LSP runners use the shared availability/install seam. These
  // managed tools are missing-command repair candidates; probe failures still
  // fail closed before this policy is consulted.
  ["pyright", { gate: "smart-default", autoInstall: true }],
  ["shellcheck", { gate: "smart-default", autoInstall: true }],
  ["shfmt", { gate: "smart-default", autoInstall: true }],
  ["tflint", { gate: "smart-default", autoInstall: true }],
  ["terragrunt", { gate: "smart-default", autoInstall: true }],
  ["trivy", { gate: "config-first", autoInstall: true }],
  ["biome", { gate: "smart-default", autoInstall: true }],
  ["ruff", { gate: "smart-default", autoInstall: true }],
  ["jscpd", { gate: "smart-default", autoInstall: true }],
  ["madge", { gate: "smart-default", autoInstall: true }],
  ["knip", { gate: "smart-default", autoInstall: true }],
  ["ast-grep", { gate: "smart-default", autoInstall: true }],
  ["oxlint", { gate: "smart-default", autoInstall: true }],
  ["stylelint", { gate: "smart-default", autoInstall: true }],
  ["sqlfluff", { gate: "smart-default", autoInstall: true }],
  ["rubocop", { gate: "smart-default", autoInstall: true }],
  ["yamllint", { gate: "smart-default", autoInstall: true }],
  ["actionlint", { gate: "smart-default", autoInstall: true }],
  ["markdownlint", { gate: "smart-default", autoInstall: true }],
  ["mypy", { gate: "config-first", autoInstall: true }],
  ["taplo", { gate: "smart-default", autoInstall: true }],
  ["hadolint", { gate: "smart-default", autoInstall: true }],
  ["helm", { gate: "smart-default", autoInstall: true }],
  ["htmlhint", { gate: "smart-default", autoInstall: true }],
  ["ktlint", { gate: "smart-default", autoInstall: true }],
  // ktfmt is opt-in (a project's explicit formatting choice), so it only runs
  // when its config marker is present — config-first, but auto-installable via
  // the maven-JAR strategy once elected (#129).
  ["ktfmt", { gate: "config-first", autoInstall: true }],
  ["golangci-lint", { gate: "config-first", autoInstall: true }],
  // SpotBugs is opt-in (lens-spotbugs flag) + config-first (needs a Java build
  // descriptor + compiled .class files), auto-installed via the archive
  // strategy when elected (#133).
  ["spotbugs", { gate: "config-first", autoInstall: true }],
  // Opengrep is opt-in (lens-opengrep flag or a discovered rule file) and
  // config-first, but auto-installable via the GitHub single-binary strategy
  // once elected — no login/token required, unlike Semgrep (#111).
  ["opengrep", { gate: "config-first", autoInstall: true }],
  ["phpstan", { gate: "config-first", autoInstall: false }],
  ["eslint", { gate: "config-first", autoInstall: false }],
  ["prettier", { gate: "smart-default", autoInstall: true }],
  ["vale", { gate: "config-first", autoInstall: false }],
  ["swiftlint", { gate: "smart-default", autoInstall: true }]
]);
var TOOL_COMMAND_SPECS = /* @__PURE__ */ new Map([
  [
    "eslint",
    {
      command: "eslint",
      windowsExt: ".cmd",
      versionArgs: ["--version"],
      managedToolId: "eslint"
    }
  ],
  [
    "stylelint",
    {
      command: "stylelint",
      windowsExt: ".cmd",
      versionArgs: ["--version"],
      managedToolId: "stylelint"
    }
  ],
  [
    "sqlfluff",
    {
      command: "sqlfluff",
      windowsExt: ".exe",
      versionArgs: ["--version"],
      managedToolId: "sqlfluff"
    }
  ],
  [
    "oxlint",
    {
      command: "oxlint",
      windowsExt: ".exe",
      versionArgs: ["--version"],
      managedToolId: "oxlint"
    }
  ],
  [
    "ruff",
    {
      command: "ruff",
      windowsExt: ".exe",
      versionArgs: ["--version"],
      managedToolId: "ruff"
    }
  ],
  [
    "biome",
    {
      command: "biome",
      windowsExt: ".cmd",
      versionArgs: ["--version"],
      managedToolId: "biome"
    }
  ],
  [
    "rubocop",
    {
      command: "rubocop",
      versionArgs: ["--version"],
      managedToolId: "rubocop"
    }
  ],
  [
    "yamllint",
    {
      command: "yamllint",
      windowsExt: ".exe",
      versionArgs: ["--version"],
      managedToolId: "yamllint"
    }
  ],
  [
    "actionlint",
    {
      command: "actionlint",
      windowsExt: ".exe",
      versionArgs: ["--version"],
      managedToolId: "actionlint"
    }
  ],
  [
    "markdownlint",
    {
      command: "markdownlint-cli2",
      windowsExt: ".cmd",
      versionArgs: ["--no-globs", "-"],
      managedToolId: "markdownlint"
    }
  ],
  [
    "vale",
    {
      command: "vale",
      windowsExt: ".exe",
      versionArgs: ["--version"],
      managedToolId: "vale"
    }
  ],
  [
    "swiftlint",
    {
      command: "swiftlint",
      versionArgs: ["--version"],
      managedToolId: "swiftlint"
    }
  ],
  [
    "mypy",
    {
      command: "mypy",
      versionArgs: ["--version"],
      managedToolId: "mypy"
    }
  ],
  [
    "phpstan",
    {
      command: "phpstan",
      windowsExt: ".bat",
      versionArgs: ["--version"],
      managedToolId: "phpstan"
    }
  ],
  [
    "taplo",
    {
      command: "taplo",
      windowsExt: ".exe",
      versionArgs: ["--version"],
      managedToolId: "taplo"
    }
  ],
  [
    "hadolint",
    {
      command: "hadolint",
      windowsExt: ".exe",
      versionArgs: ["--version"],
      managedToolId: "hadolint"
    }
  ],
  [
    "htmlhint",
    {
      command: "htmlhint",
      versionArgs: ["--version"],
      managedToolId: "htmlhint"
    }
  ],
  [
    "ktlint",
    {
      command: "ktlint",
      windowsExt: ".exe",
      versionArgs: ["--version"],
      managedToolId: "ktlint"
    }
  ],
  [
    "prettier",
    {
      command: "prettier",
      windowsExt: ".cmd",
      versionArgs: ["--version"],
      managedToolId: "prettier"
    }
  ]
]);
var STYLELINT_CONFIGS = [
  ".stylelintrc",
  ".stylelintrc.json",
  ".stylelintrc.jsonc",
  ".stylelintrc.yaml",
  ".stylelintrc.yml",
  ".stylelintrc.js",
  ".stylelintrc.cjs",
  "stylelint.config.js",
  "stylelint.config.cjs",
  "stylelint.config.mjs"
];
var SQLFLUFF_CONFIGS = [
  ".sqlfluff",
  "pyproject.toml",
  "setup.cfg",
  "tox.ini"
];
var RUBOCOP_CONFIGS = [".rubocop.yml", ".rubocop.yaml"];
var MYPY_CONFIGS = ["mypy.ini", ".mypy.ini", "setup.cfg", "pyproject.toml"];
var YAMLLINT_CONFIGS = [
  ".yamllint",
  ".yamllint.yml",
  ".yamllint.yaml",
  "pyproject.toml",
  "setup.cfg",
  "tox.ini"
];
var MARKDOWNLINT_CONFIGS = [
  // markdownlint-cli2's built-in config names (keep this list in sync with
  // the installed CLI's supported configuration file names).
  ".markdownlint-cli2.jsonc",
  ".markdownlint-cli2.yaml",
  ".markdownlint-cli2.yml",
  ".markdownlint-cli2.cjs",
  ".markdownlint-cli2.mjs",
  ".markdownlint.jsonc",
  ".markdownlint.json",
  ".markdownlint.yaml",
  ".markdownlint.yml",
  ".markdownlint.cjs",
  ".markdownlint.mjs",
  // Retain the legacy detector name for compatibility with existing policy
  // behavior, even though current markdownlint-cli2 versions do not prefer it.
  ".markdownlintrc"
];
var PRETTIER_CONFIGS = [
  ".prettierrc",
  ".prettierrc.json",
  ".prettierrc.yml",
  ".prettierrc.yaml",
  ".prettierrc.js",
  ".prettierrc.cjs",
  ".prettierrc.mjs",
  "prettier.config.js",
  "prettier.config.cjs",
  "prettier.config.mjs",
  "prettier.config.ts"
];
var RUFF_PROJECT_CONFIGS = ["ruff.toml", ".ruff.toml"];
var GOLANGCI_CONFIGS = [
  ".golangci.yml",
  ".golangci.yaml",
  ".golangci.toml",
  ".golangci.json"
];
var PHPSTAN_CONFIGS = [
  "phpstan.neon",
  "phpstan.neon.dist",
  "phpstan.dist.neon"
];
var VITE_CONFIGS = [
  "vite.config.ts",
  "vite.config.mts",
  "vite.config.cts",
  "vite.config.js",
  "vite.config.mjs",
  "vite.config.cjs"
];
function getLinterPolicyForFile(filePath, context = {}) {
  const ext = path.extname(filePath).toLowerCase();
  if ([".js", ".jsx", ".ts", ".tsx", ".mjs", ".cjs"].includes(ext)) {
    const policy = getJstsLintPolicy({
      hasEslintConfig: context.hasEslintConfig,
      hasOxlintConfig: context.hasOxlintConfig,
      hasBiomeConfig: context.hasBiomeConfig
    });
    return {
      runnerNames: ["eslint", "oxlint", "biome-check-json"],
      preferredRunners: policy.preferredRunners,
      defaultRunner: policy.preferredRunners[0],
      defaultWhenUnconfigured: !policy.hasEslintConfig && !policy.hasOxlintConfig,
      gate: policy.hasEslintConfig ? "config-first" : "smart-default"
    };
  }
  if ([".py", ".pyi"].includes(ext)) {
    const preferredRunners = ["ruff-lint"];
    if (context.hasMypyConfig)
      preferredRunners.push("mypy");
    return {
      runnerNames: ["ruff-lint", "mypy"],
      preferredRunners,
      defaultRunner: "ruff-lint",
      defaultWhenUnconfigured: true,
      gate: context.hasMypyConfig ? "mixed" : "smart-default"
    };
  }
  if ([".css", ".scss", ".sass", ".less"].includes(ext)) {
    return {
      runnerNames: ["stylelint"],
      preferredRunners: ["stylelint"],
      defaultRunner: "stylelint",
      defaultWhenUnconfigured: true,
      gate: "smart-default"
    };
  }
  if (ext === ".sql") {
    return {
      runnerNames: ["sqlfluff"],
      preferredRunners: ["sqlfluff"],
      defaultRunner: "sqlfluff",
      defaultWhenUnconfigured: false,
      gate: "config-first"
    };
  }
  if ([".rb", ".rake", ".gemspec", ".ru"].includes(ext)) {
    return {
      runnerNames: ["rubocop"],
      preferredRunners: ["rubocop"],
      defaultRunner: "rubocop",
      defaultWhenUnconfigured: true,
      gate: "smart-default"
    };
  }
  if ([".yaml", ".yml"].includes(ext)) {
    return {
      runnerNames: ["yamllint"],
      preferredRunners: ["yamllint"],
      defaultRunner: "yamllint",
      defaultWhenUnconfigured: true,
      gate: "smart-default"
    };
  }
  if ([".md", ".mdx"].includes(ext)) {
    return {
      runnerNames: ["markdownlint"],
      preferredRunners: ["markdownlint"],
      defaultRunner: "markdownlint",
      defaultWhenUnconfigured: true,
      gate: "smart-default"
    };
  }
  if ([".html", ".htm"].includes(ext)) {
    return {
      runnerNames: ["htmlhint"],
      preferredRunners: ["htmlhint"],
      defaultRunner: "htmlhint",
      defaultWhenUnconfigured: true,
      gate: "smart-default"
    };
  }
  if (path.basename(filePath).toLowerCase() === "dockerfile") {
    return {
      runnerNames: ["hadolint"],
      preferredRunners: ["hadolint"],
      defaultRunner: "hadolint",
      defaultWhenUnconfigured: true,
      gate: "smart-default"
    };
  }
  if (TERRAGRUNT_FILENAMES.includes(path.basename(filePath).toLowerCase())) {
    return {
      runnerNames: ["terragrunt"],
      preferredRunners: ["terragrunt"],
      defaultRunner: "terragrunt",
      defaultWhenUnconfigured: true,
      gate: "smart-default"
    };
  }
  if ([".kt", ".kts"].includes(ext)) {
    const preferredRunners = [];
    if (!context.hasKtfmtConfig)
      preferredRunners.push("ktlint");
    if (context.hasDetektConfig)
      preferredRunners.push("detekt");
    return {
      runnerNames: ["ktlint", "detekt"],
      preferredRunners,
      defaultRunner: preferredRunners[0],
      defaultWhenUnconfigured: true,
      gate: context.hasKtfmtConfig ? "config-first" : context.hasDetektConfig ? "mixed" : "smart-default"
    };
  }
  if (ext === ".toml") {
    return {
      runnerNames: ["taplo"],
      preferredRunners: ["taplo"],
      defaultRunner: "taplo",
      defaultWhenUnconfigured: true,
      gate: "smart-default"
    };
  }
  if (ext === ".go") {
    return {
      runnerNames: ["golangci-lint"],
      preferredRunners: context.hasGolangciConfig ? ["golangci-lint"] : [],
      defaultRunner: "golangci-lint",
      defaultWhenUnconfigured: false,
      gate: "config-first"
    };
  }
  if (ext === ".php") {
    return {
      runnerNames: ["phpstan"],
      preferredRunners: context.hasPhpstanConfig ? ["phpstan"] : [],
      defaultRunner: "phpstan",
      defaultWhenUnconfigured: false,
      gate: "config-first"
    };
  }
  if (ext === ".rs") {
    return {
      runnerNames: ["rust-clippy"],
      preferredRunners: ["rust-clippy"],
      defaultRunner: "rust-clippy",
      defaultWhenUnconfigured: true,
      gate: "smart-default"
    };
  }
  if ([".sh", ".bash"].includes(ext)) {
    return {
      runnerNames: ["shellcheck"],
      preferredRunners: ["shellcheck"],
      defaultRunner: "shellcheck",
      defaultWhenUnconfigured: true,
      gate: "smart-default"
    };
  }
  if (ext === ".fish") {
    return {
      runnerNames: ["fish-indent"],
      preferredRunners: ["fish-indent"],
      defaultRunner: "fish-indent",
      defaultWhenUnconfigured: true,
      gate: "smart-default"
    };
  }
  if ([".tf", ".tfvars"].includes(ext)) {
    return {
      runnerNames: ["tflint"],
      preferredRunners: ["tflint"],
      defaultRunner: "tflint",
      defaultWhenUnconfigured: true,
      gate: context.hasTflintConfig ? "config-first" : "smart-default"
    };
  }
  if ([".ex", ".exs", ".eex", ".heex", ".leex"].includes(ext)) {
    return {
      runnerNames: ["credo"],
      preferredRunners: ["credo"],
      defaultRunner: "credo",
      defaultWhenUnconfigured: true,
      gate: "smart-default"
    };
  }
  if ([".c", ".cc", ".cpp", ".cxx", ".h", ".hpp", ".ino"].includes(ext)) {
    return {
      runnerNames: ["cpp-check"],
      preferredRunners: ["cpp-check"],
      defaultRunner: "cpp-check",
      defaultWhenUnconfigured: true,
      gate: "smart-default"
    };
  }
  if (ext === ".dart") {
    return {
      runnerNames: ["dart-analyze"],
      preferredRunners: ["dart-analyze"],
      defaultRunner: "dart-analyze",
      defaultWhenUnconfigured: true,
      gate: "smart-default"
    };
  }
  if (ext === ".gleam") {
    return {
      runnerNames: ["gleam-check"],
      preferredRunners: ["gleam-check"],
      defaultRunner: "gleam-check",
      defaultWhenUnconfigured: true,
      gate: "smart-default"
    };
  }
  if ([".ps1", ".psm1", ".psd1"].includes(ext)) {
    return {
      runnerNames: ["psscriptanalyzer"],
      preferredRunners: ["psscriptanalyzer"],
      defaultRunner: "psscriptanalyzer",
      defaultWhenUnconfigured: true,
      gate: "smart-default"
    };
  }
  if (ext === ".prisma") {
    return {
      runnerNames: ["prisma-validate"],
      preferredRunners: ["prisma-validate"],
      defaultRunner: "prisma-validate",
      defaultWhenUnconfigured: true,
      gate: "smart-default"
    };
  }
  return void 0;
}
function getCachedProjectConventions(cwd) {
  const snapshot = loadProjectSnapshotWithoutWordIndex(cwd);
  return snapshot?.conventions;
}
function getLinterPolicyForCwd(filePath, cwd) {
  const context = {
    hasEslintConfig: hasEslintConfig(cwd),
    hasOxlintConfig: hasOxlintConfig(cwd),
    hasBiomeConfig: hasBiomeConfig(cwd),
    hasStylelintConfig: hasStylelintConfig(cwd),
    hasSqlfluffConfig: hasSqlfluffConfig(cwd),
    hasRubocopConfig: hasRubocopConfig(cwd),
    hasYamllintConfig: hasYamllintConfig(cwd),
    hasMarkdownlintConfig: hasMarkdownlintConfig(cwd),
    hasGolangciConfig: hasGolangciConfig(cwd),
    hasPhpstanConfig: hasPhpstanConfig(cwd),
    hasMypyConfig: hasMypyConfig(cwd),
    hasDetektConfig: hasDetektConfig(cwd),
    hasKtfmtConfig: hasKtfmtConfig(cwd),
    // From the file's directory, not cwd: a `.tflint.hcl` in a terraform
    // subdirectory is invisible to an upward walk that starts at the repo root.
    hasTflintConfig: hasTflintConfig(path.dirname(path.resolve(cwd, filePath)))
  };
  const policy = getLinterPolicyForFile(filePath, context);
  if (policy) {
    const conventions = getCachedProjectConventions(cwd);
    if (conventions && conventions.frameworks.length > 0) {
      policy.frameworkHints = conventions.frameworks.map((f) => f.id);
    }
  }
  logLatency({
    type: "phase",
    phase: "linter_selected",
    filePath,
    durationMs: 0,
    metadata: {
      runner: policy?.defaultRunner ?? null,
      gate: policy?.gate ?? null,
      cwd,
      context,
      frameworkHints: policy?.frameworkHints ?? null
    }
  });
  return policy;
}
var ESLINT_FLAT_CONFIGS = [
  "eslint.config.js",
  "eslint.config.mjs",
  "eslint.config.cjs",
  "eslint.config.ts",
  "eslint.config.mts",
  "eslint.config.cts"
];
var ESLINT_LEGACY_CONFIGS = [
  ".eslintrc",
  ".eslintrc.js",
  ".eslintrc.cjs",
  ".eslintrc.json",
  ".eslintrc.yaml",
  ".eslintrc.yml"
];
function* walkUpDirsUntilPackageJson(cwd) {
  for (const dir of walkUpDirs(cwd)) {
    yield dir;
    if (fs.existsSync(path.join(dir, "package.json")))
      return;
  }
}
function findNearestPackageJsonPath(cwd) {
  let dir = cwd;
  const root = path.parse(dir).root;
  while (true) {
    const pkgPath = path.join(dir, "package.json");
    if (fs.existsSync(pkgPath))
      return pkgPath;
    if (dir === root)
      break;
    const parent = path.dirname(dir);
    if (parent === dir)
      break;
    dir = parent;
  }
  return void 0;
}
function hasNearestPackageJsonDependency(cwd, dependencyName) {
  const pkgPath = findNearestPackageJsonPath(cwd);
  if (!pkgPath)
    return false;
  try {
    const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
    return Boolean(pkg.dependencies?.[dependencyName] ?? pkg.devDependencies?.[dependencyName]);
  } catch {
  }
  return false;
}
function hasNearestPackageJsonField(cwd, fieldName) {
  const pkgPath = findNearestPackageJsonPath(cwd);
  if (!pkgPath)
    return false;
  try {
    const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
    return pkg[fieldName] !== void 0;
  } catch {
  }
  return false;
}
function hasEslintConfig(cwd) {
  for (const dir of walkUpDirs(cwd)) {
    for (const cfg of ESLINT_FLAT_CONFIGS) {
      if (fs.existsSync(path.join(dir, cfg)))
        return true;
    }
  }
  for (const dir of walkUpDirsUntilPackageJson(cwd)) {
    for (const cfg of ESLINT_LEGACY_CONFIGS) {
      if (fs.existsSync(path.join(dir, cfg)))
        return true;
    }
    const pkgPath = path.join(dir, "package.json");
    if (fs.existsSync(pkgPath)) {
      try {
        if (JSON.parse(fs.readFileSync(pkgPath, "utf-8")).eslintConfig)
          return true;
      } catch {
      }
    }
  }
  return false;
}
function hasBiomeConfig(cwd) {
  return getBiomeConfigPath(cwd) !== void 0;
}
function getBiomeConfigPath(cwd) {
  for (const dir of walkUpDirs(cwd)) {
    const jsoncPath = path.join(dir, "biome.jsonc");
    if (fs.existsSync(jsoncPath))
      return jsoncPath;
    const jsonPath = path.join(dir, "biome.json");
    if (fs.existsSync(jsonPath))
      return jsonPath;
  }
  return void 0;
}
function hasOxfmtConfig(cwd) {
  for (const dir of walkUpDirs(cwd)) {
    if (fs.existsSync(path.join(dir, "oxfmt.toml")))
      return true;
    if (fs.existsSync(path.join(dir, ".oxfmtrc.json")))
      return true;
    if (hasVitePlusConfig(dir))
      return true;
    const pkgPath = path.join(dir, "package.json");
    if (fs.existsSync(pkgPath)) {
      try {
        const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
        const deps = {
          ...pkg.dependencies,
          ...pkg.devDependencies
        };
        if (deps["oxfmt"] || deps["@oxc-project/oxfmt"])
          return true;
      } catch {
      }
    }
  }
  return false;
}
var OXFMT_SVELTE_TOML_TRUE = /(^|\n)\s*svelte\s*=\s*true\s*(\s*#.*)?(\r?\n|$)/;
function hasOxfmtSvelteConfig(cwd) {
  if (!hasNearestPackageJsonDependency(cwd, "svelte"))
    return false;
  for (const dir of walkUpDirs(cwd)) {
    const rcPath = path.join(dir, ".oxfmtrc.json");
    if (fs.existsSync(rcPath)) {
      try {
        const cfg = JSON.parse(fs.readFileSync(rcPath, "utf-8"));
        if (cfg.svelte === true)
          return true;
      } catch {
      }
    }
    const tomlPath = path.join(dir, "oxfmt.toml");
    if (fs.existsSync(tomlPath)) {
      try {
        const content = fs.readFileSync(tomlPath, "utf-8");
        if (OXFMT_SVELTE_TOML_TRUE.test(content))
          return true;
      } catch {
      }
    }
  }
  return false;
}
function hasStylelintConfig(cwd) {
  for (const dir of walkUpDirs(cwd)) {
    if (STYLELINT_CONFIGS.some((cfg) => fs.existsSync(path.join(dir, cfg)))) {
      return true;
    }
    const pkgPath = path.join(dir, "package.json");
    if (fs.existsSync(pkgPath)) {
      try {
        const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
        if (pkg.stylelint)
          return true;
      } catch {
      }
    }
  }
  return false;
}
function hasSqlfluffConfig(cwd) {
  for (const dir of walkUpDirs(cwd)) {
    for (const cfg of SQLFLUFF_CONFIGS) {
      const cfgPath = path.join(dir, cfg);
      if (!fs.existsSync(cfgPath))
        continue;
      if (cfg === "pyproject.toml") {
        try {
          const content = fs.readFileSync(cfgPath, "utf-8");
          if (content.includes("[tool.sqlfluff]"))
            return true;
        } catch {
        }
        continue;
      }
      if (cfg === "setup.cfg" || cfg === "tox.ini") {
        try {
          const content = fs.readFileSync(cfgPath, "utf-8");
          if (content.includes("[sqlfluff]"))
            return true;
        } catch {
        }
        continue;
      }
      return true;
    }
  }
  for (const dir of walkUpDirs(cwd)) {
    for (const depFile of ["requirements.txt", "Pipfile", "pyproject.toml"]) {
      const depPath = path.join(dir, depFile);
      if (!fs.existsSync(depPath))
        continue;
      try {
        const content = fs.readFileSync(depPath, "utf-8").toLowerCase();
        if (content.includes("sqlfluff"))
          return true;
      } catch {
      }
    }
  }
  return false;
}
function hasRubocopConfig(cwd) {
  for (const dir of walkUpDirs(cwd)) {
    for (const cfg of RUBOCOP_CONFIGS) {
      if (fs.existsSync(path.join(dir, cfg)))
        return true;
    }
    const gemfile = path.join(dir, "Gemfile");
    if (fs.existsSync(gemfile)) {
      try {
        const content = fs.readFileSync(gemfile, "utf-8");
        if (content.includes("rubocop"))
          return true;
      } catch {
      }
    }
  }
  return false;
}
function hasMypyConfig(cwd) {
  for (const dir of walkUpDirs(cwd)) {
    for (const cfg of MYPY_CONFIGS) {
      const cfgPath = path.join(dir, cfg);
      if (!fs.existsSync(cfgPath))
        continue;
      if (cfg === "setup.cfg") {
        try {
          if (fs.readFileSync(cfgPath, "utf-8").includes("[mypy]"))
            return true;
        } catch {
        }
        continue;
      }
      if (cfg === "pyproject.toml") {
        try {
          if (fs.readFileSync(cfgPath, "utf-8").includes("[tool.mypy]"))
            return true;
        } catch {
        }
        continue;
      }
      return true;
    }
  }
  return false;
}
function hasYamllintConfig(cwd) {
  for (const dir of walkUpDirs(cwd)) {
    for (const cfg of YAMLLINT_CONFIGS) {
      const cfgPath = path.join(dir, cfg);
      if (!fs.existsSync(cfgPath))
        continue;
      if (cfg === "pyproject.toml") {
        try {
          const content = fs.readFileSync(cfgPath, "utf-8");
          if (content.includes("[tool.yamllint]"))
            return true;
        } catch {
        }
        continue;
      }
      if (cfg === "setup.cfg" || cfg === "tox.ini") {
        try {
          const content = fs.readFileSync(cfgPath, "utf-8");
          if (content.includes("[yamllint]"))
            return true;
        } catch {
        }
        continue;
      }
      return true;
    }
  }
  for (const dir of walkUpDirs(cwd)) {
    for (const depFile of ["requirements.txt", "Pipfile", "pyproject.toml"]) {
      const depPath = path.join(dir, depFile);
      if (!fs.existsSync(depPath))
        continue;
      try {
        const content = fs.readFileSync(depPath, "utf-8").toLowerCase();
        if (content.includes("yamllint"))
          return true;
      } catch {
      }
    }
  }
  return false;
}
function hasMarkdownlintConfig(cwd) {
  return findNearestContaining(cwd, MARKDOWNLINT_CONFIGS) !== void 0;
}
function hasPrettierConfig(cwd) {
  for (const dir of walkUpDirs(cwd)) {
    if (PRETTIER_CONFIGS.some((cfg) => fs.existsSync(path.join(dir, cfg))))
      return true;
    const pkgPath = path.join(dir, "package.json");
    if (fs.existsSync(pkgPath)) {
      try {
        const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
        if (Object.hasOwn(pkg, "prettier"))
          return true;
      } catch {
      }
    }
  }
  return false;
}
function hasBlackConfig(cwd) {
  for (const dir of walkUpDirs(cwd)) {
    const pyproject = path.join(dir, "pyproject.toml");
    if (fs.existsSync(pyproject)) {
      try {
        if (fs.readFileSync(pyproject, "utf-8").includes("[tool.black]"))
          return true;
      } catch {
      }
    }
  }
  for (const dir of walkUpDirs(cwd)) {
    for (const depFile of ["requirements.txt", "Pipfile"]) {
      const depPath = path.join(dir, depFile);
      if (!fs.existsSync(depPath))
        continue;
      try {
        if (fs.readFileSync(depPath, "utf-8").toLowerCase().includes("black"))
          return true;
      } catch {
      }
    }
  }
  return false;
}
function hasRuffConfig(cwd) {
  for (const dir of walkUpDirs(cwd)) {
    for (const cfg of RUFF_PROJECT_CONFIGS) {
      if (fs.existsSync(path.join(dir, cfg)))
        return true;
    }
    const pyproject = path.join(dir, "pyproject.toml");
    if (fs.existsSync(pyproject)) {
      try {
        if (fs.readFileSync(pyproject, "utf-8").includes("[tool.ruff]"))
          return true;
      } catch {
      }
    }
  }
  return false;
}
function configArgsWithFallback(hasConfig, toolArgs, packageConfigPath) {
  return hasConfig ? [] : [...toolArgs, resolvePackagePath(import.meta.url, packageConfigPath)];
}
function markdownlintConfigArgs(cwd) {
  return configArgsWithFallback(hasMarkdownlintConfig(cwd), ["--config"], "config/markdownlint/core.json");
}
function ruffConfigArgs(cwd) {
  return configArgsWithFallback(hasRuffConfig(cwd), ["--config"], "config/ruff/core.toml");
}
function biomeConfigArgs(cwd) {
  if (getBiomeConfigPath(cwd))
    return [];
  return [
    "--config-path=" + resolvePackagePath(import.meta.url, "config/biome/core.jsonc")
  ];
}
function hasGolangciConfig(cwd) {
  return findNearestContaining(cwd, GOLANGCI_CONFIGS) !== void 0;
}
function hasTflintConfig(startDir) {
  return findNearestContaining(startDir, [".tflint.hcl"]) !== void 0;
}
function hasClangFormatConfig(cwd) {
  return findNearestContaining(cwd, [".clang-format", "_clang-format"]) !== void 0;
}
function hasPhpCsFixerConfig(cwd) {
  return findNearestContaining(cwd, [
    ".php-cs-fixer.php",
    ".php-cs-fixer.dist.php"
  ]) !== void 0;
}
function hasStyluaConfig(cwd) {
  return findNearestContaining(cwd, ["stylua.toml", ".stylua.toml"]) !== void 0;
}
function hasOcamlformatConfig(cwd) {
  return findNearestContaining(cwd, [".ocamlformat"]) !== void 0;
}
function hasGoogleJavaFormatConfig(cwd) {
  return findNearestContaining(cwd, [".google-java-format", ".editorconfig"]) !== void 0;
}
function hasCljfmtConfig(cwd) {
  return findNearestContaining(cwd, [".cljfmt.edn", "cljfmt.edn", ".cljfmt"]) !== void 0;
}
function hasCmakeFormatConfig(cwd) {
  return findNearestContaining(cwd, [
    ".cmake-format",
    ".cmake-format.yaml",
    ".cmake-format.yml",
    ".cmake-format.json",
    ".cmake-format.py",
    "cmake-format.yaml",
    "cmake-format.yml"
  ]) !== void 0;
}
var PSSCRIPTANALYZER_SETTINGS_FILES = [
  "PSScriptAnalyzerSettings.psd1",
  "ScriptAnalyzerSettings.psd1"
];
function findPSScriptAnalyzerConfigPath(cwd) {
  for (const dir of walkUpDirs(cwd)) {
    for (const name of PSSCRIPTANALYZER_SETTINGS_FILES) {
      const candidate = path.join(dir, name);
      if (fs.existsSync(candidate))
        return candidate;
    }
  }
  return void 0;
}
function hasPSScriptAnalyzerConfig(cwd) {
  return findPSScriptAnalyzerConfigPath(cwd) !== void 0;
}
function hasCsharpierConfig(cwd) {
  return findNearestContaining(cwd, [
    ".csharpierrc",
    ".csharpierrc.json",
    ".csharpierrc.yaml",
    ".csharpierrc.yml"
  ]) !== void 0;
}
function hasOrmoluConfig(cwd) {
  return findNearestContaining(cwd, [".ormolu"]) !== void 0;
}
function hasTaploConfig(cwd) {
  return findNearestContaining(cwd, ["taplo.toml", ".taplo.toml"]) !== void 0;
}
function hasTerraformConfig(cwd) {
  return findNearestContaining(cwd, [".terraform.lock.hcl"]) !== void 0;
}
function hasSwiftformatConfig(cwd) {
  return findNearestContaining(cwd, [".swiftformat"]) !== void 0;
}
function hasFantomasConfig(cwd) {
  return findNearestContaining(cwd, [".fantomasignore", ".editorconfig"]) !== void 0;
}
function hasMixFormatConfig(cwd) {
  return findNearestContaining(cwd, [".formatter.exs"]) !== void 0;
}
function hasPhpstanConfig(cwd) {
  return findNearestContaining(cwd, PHPSTAN_CONFIGS) !== void 0;
}
var DETEKT_CONFIGS = [
  "detekt.yml",
  ".detekt.yml",
  path.join("config", "detekt", "detekt.yml"),
  path.join("detekt", "detekt.yml")
];
function hasDetektConfig(cwd) {
  for (const dir of walkUpDirs(cwd)) {
    if (DETEKT_CONFIGS.some((cfg) => fs.existsSync(path.join(dir, cfg))))
      return true;
  }
  return false;
}
var KTFMT_CONFIG_FILES = [".ktfmt", ".ktfmt.kts"];
var KTFMT_GRADLE_FILES = [
  "build.gradle.kts",
  "build.gradle",
  "settings.gradle.kts",
  "settings.gradle"
];
var KOTLIN_GRADLE_FILES = [
  "build.gradle.kts",
  "build.gradle",
  "settings.gradle.kts",
  "settings.gradle"
];
var GRADLE_KTLINT_PLUGIN_PATTERN = /(?:id\s*\(\s*|id\s+|apply\s+plugin\s*:\s*)["']org\.jlleitschuh\.gradle\.ktlint["']/g;
var GRADLE_BUILD_LOGIC_DIRS = ["buildSrc", "build-logic"];
var GRADLE_BUILD_LOGIC_EXTENSIONS = [".gradle", ".gradle.kts", ".kt"];
var GRADLE_INCLUDE_BUILD_PATTERN = /\bincludeBuild\s*\(\s*["']([^"']+)["']/g;
var GRADLE_BUILD_LOGIC_SCAN_MAX_ENTRIES = 1e4;
var spotlessKotlinConfigCache = /* @__PURE__ */ new Map();
var spotlessGradleReadCount = 0;
function stripGradleCommentsAndStrings(source) {
  let result = "";
  let state = "code";
  let quote = "";
  for (let index = 0; index < source.length; index += 1) {
    const char = source[index];
    const next = source[index + 1];
    if (state === "code") {
      if (char === "/" && next === "/") {
        result += "  ";
        index += 1;
        state = "line-comment";
      } else if (char === "/" && next === "*") {
        result += "  ";
        index += 1;
        state = "block-comment";
      } else if (char === '"' || char === "'") {
        result += " ";
        quote = char;
        state = "string";
      } else {
        result += char;
      }
      continue;
    }
    if (state === "line-comment") {
      if (char === "\n" || char === "\r") {
        result += char;
        state = "code";
      } else {
        result += " ";
      }
      continue;
    }
    if (state === "block-comment") {
      if (char === "*" && next === "/") {
        result += "  ";
        index += 1;
        state = "code";
      } else {
        result += char === "\n" || char === "\r" ? char : " ";
      }
      continue;
    }
    if (char === "\\") {
      result += " ";
      if (index + 1 < source.length) {
        result += source[index + 1] === "\n" ? "\n" : " ";
        index += 1;
      }
    } else if (char === quote) {
      result += " ";
      state = "code";
    } else {
      result += char === "\n" || char === "\r" ? char : " ";
    }
  }
  return result;
}
var GRADLE_IDENTIFIER_CHAR = /[A-Za-z0-9_$]/;
function gradleBlockNameBefore(source, open) {
  let index = open - 1;
  while (index >= 0 && /\s/.test(source[index]))
    index -= 1;
  const end = index + 1;
  while (index >= 0 && GRADLE_IDENTIFIER_CHAR.test(source[index]))
    index -= 1;
  return source.slice(index + 1, end);
}
function gradleBlockRanges(source) {
  const ranges = [];
  const opens = [];
  for (let index = 0; index < source.length; index += 1) {
    const char = source[index];
    if (char === "{") {
      opens.push(index);
      continue;
    }
    if (char !== "}")
      continue;
    const open = opens.pop();
    if (open === void 0)
      continue;
    ranges.push({
      name: gradleBlockNameBefore(source, open),
      start: open + 1,
      end: index
    });
  }
  return ranges.sort((a, b) => a.start - b.start);
}
function namedGradleBlockRanges(source, name) {
  return gradleBlockRanges(source).filter((range) => range.name === name);
}
function namedGradleBlockBodies(source, name) {
  return namedGradleBlockRanges(source, name).map((range) => source.slice(range.start, range.end));
}
function getSpotlessKotlinFormatter(cwd) {
  for (const dir of walkUpDirs(cwd)) {
    for (const gradle of KOTLIN_GRADLE_FILES) {
      const filePath = path.join(dir, gradle);
      if (!fs.existsSync(filePath))
        continue;
      try {
        const mtime = fs.statSync(filePath).mtimeMs;
        let config = spotlessKotlinConfigCache.get(filePath);
        if (!config || config.mtime !== mtime) {
          const source = stripGradleCommentsAndStrings(fs.readFileSync(filePath, "utf-8"));
          spotlessGradleReadCount += 1;
          const kotlinBodies = namedGradleBlockBodies(source, "spotless").flatMap((spotless) => namedGradleBlockBodies(spotless, "kotlin"));
          config = {
            mtime,
            ktlintConfig: kotlinBodies.some((body) => /\bktlint\s*\(/.test(body)),
            ktfmtConfig: kotlinBodies.some((body) => /\bktfmt\s*\(/.test(body))
          };
          spotlessKotlinConfigCache.set(filePath, config);
        }
        if (config.ktlintConfig)
          return "ktlint";
        if (config.ktfmtConfig)
          return "ktfmt";
      } catch {
      }
    }
  }
  return void 0;
}
function hasKtlintConfig(cwd) {
  return getSpotlessKotlinFormatter(cwd) === "ktlint";
}
function hasGradleKtlintPlugin(cwd) {
  const files = /* @__PURE__ */ new Set();
  const scan = { entries: 0, exceeded: false, indeterminate: false };
  for (const dir of walkUpDirs(cwd)) {
    for (const gradle of KOTLIN_GRADLE_FILES) {
      const filePath = path.join(dir, gradle);
      if (fs.existsSync(filePath))
        files.add(filePath);
    }
    for (const buildLogicDir of GRADLE_BUILD_LOGIC_DIRS) {
      if (!addGradleBuildLogicFiles(path.join(dir, buildLogicDir), files, scan)) {
        scan.exceeded = true;
      }
    }
    for (const settings of ["settings.gradle.kts", "settings.gradle"]) {
      const settingsPath = path.join(dir, settings);
      if (!fs.existsSync(settingsPath))
        continue;
      try {
        const raw = fs.readFileSync(settingsPath, "utf-8");
        const stripped = stripGradleCommentsAndStrings(raw);
        GRADLE_INCLUDE_BUILD_PATTERN.lastIndex = 0;
        let match;
        while ((match = GRADLE_INCLUDE_BUILD_PATTERN.exec(raw)) !== null) {
          const code = stripped.slice(match.index, match.index + match[0].length);
          const includedBuild = match[1];
          if (includedBuild && /^\s*includeBuild\s*\(\s*/.test(code)) {
            if (!addGradleBuildLogicFiles(path.resolve(dir, includedBuild), files, scan)) {
              scan.exceeded = true;
            }
          }
        }
      } catch {
        scan.indeterminate = true;
      }
    }
  }
  if (scan.exceeded) {
    recordDegradationOnce({
      kind: "gradle-ktlint-scan-budget-exceeded",
      subject: "ktlint:gradle-build-logic",
      reason: `Gradle build-logic ownership scan exceeded its ${GRADLE_BUILD_LOGIC_SCAN_MAX_ENTRIES}-entry budget; ownership cannot be established, so ktlint autofix is declined`
    });
    return { kind: "indeterminate" };
  }
  if (scan.indeterminate)
    return { kind: "indeterminate" };
  for (const filePath of files) {
    try {
      const raw = fs.readFileSync(filePath, "utf-8");
      const stripped = stripGradleCommentsAndStrings(raw);
      GRADLE_KTLINT_PLUGIN_PATTERN.lastIndex = 0;
      let match;
      while ((match = GRADLE_KTLINT_PLUGIN_PATTERN.exec(raw)) !== null) {
        const code = stripped.slice(match.index, match.index + match[0].length);
        if (!/^\s*$/.test(code))
          return { kind: "owned" };
      }
    } catch {
      return { kind: "indeterminate" };
    }
  }
  return { kind: "not-owned" };
}
function addGradleBuildLogicFiles(root, files, scan) {
  if (!fs.existsSync(root))
    return true;
  const pending = [root];
  while (pending.length > 0) {
    const dir = pending.pop();
    if (!dir)
      continue;
    let entries;
    try {
      entries = fs.readdirSync(dir, { withFileTypes: true });
    } catch {
      scan.indeterminate = true;
      continue;
    }
    for (const entry of entries) {
      scan.entries += 1;
      if (scan.entries > GRADLE_BUILD_LOGIC_SCAN_MAX_ENTRIES)
        return false;
      const entryPath = path.join(dir, entry.name);
      if (entry.isDirectory()) {
        pending.push(entryPath);
      } else if (entry.isFile() && GRADLE_BUILD_LOGIC_EXTENSIONS.some((extension) => entry.name.endsWith(extension))) {
        files.add(entryPath);
      }
    }
  }
  return true;
}
function hasKtfmtConfig(cwd) {
  const spotlessFormatter = getSpotlessKotlinFormatter(cwd);
  if (spotlessFormatter)
    return spotlessFormatter === "ktfmt";
  for (const dir of walkUpDirs(cwd)) {
    if (KTFMT_CONFIG_FILES.some((cfg) => fs.existsSync(path.join(dir, cfg))))
      return true;
    for (const gradle of KTFMT_GRADLE_FILES) {
      const p = path.join(dir, gradle);
      if (fs.existsSync(p)) {
        try {
          if (/ktfmt|com\.facebook\.ktfmt/i.test(fs.readFileSync(p, "utf-8")))
            return true;
        } catch {
        }
      }
    }
  }
  return false;
}
var JAVA_BUILD_DESCRIPTORS = [
  "pom.xml",
  "build.gradle",
  "build.gradle.kts",
  "settings.gradle",
  "settings.gradle.kts"
];
var COMPILED_CLASSES_DIRS = [
  path.join("target", "classes"),
  path.join("build", "classes"),
  path.join("out", "production"),
  path.join("bin", "main")
];
function hasJavaBuildDescriptor(cwd, ceiling) {
  const normalizedCeiling = ceiling ? normalizeMapKey(path.resolve(ceiling)) : void 0;
  for (const dir of walkUpDirs(cwd)) {
    if (JAVA_BUILD_DESCRIPTORS.some((d) => fs.existsSync(path.join(dir, d))))
      return true;
    if (normalizedCeiling && normalizeMapKey(path.resolve(dir)) === normalizedCeiling)
      break;
  }
  return false;
}
function findCompiledClassesDir(cwd) {
  for (const dir of walkUpDirs(cwd)) {
    for (const rel of COMPILED_CLASSES_DIRS) {
      const candidate = path.join(dir, rel);
      if (fs.existsSync(candidate))
        return candidate;
    }
  }
  return void 0;
}
function hasStandardrbConfig(cwd) {
  for (const dir of walkUpDirs(cwd)) {
    const gemfile = path.join(dir, "Gemfile");
    if (fs.existsSync(gemfile)) {
      try {
        if (fs.readFileSync(gemfile, "utf-8").includes("standard"))
          return true;
      } catch {
      }
    }
  }
  return false;
}
function getRubocopCommand(cwd) {
  const gemfile = path.join(cwd, "Gemfile");
  if (fs.existsSync(gemfile)) {
    try {
      const content = fs.readFileSync(gemfile, "utf-8");
      if (content.includes("rubocop")) {
        return { cmd: "bundle", args: ["exec", "rubocop"] };
      }
    } catch {
    }
  }
  return { cmd: "rubocop", args: [] };
}
function hasVitePlusConfig(cwd) {
  for (const dir of walkUpDirs(cwd)) {
    if (fs.existsSync(path.join(dir, "vite-plus.json")))
      return true;
    const pkgPath = path.join(dir, "package.json");
    if (fs.existsSync(pkgPath)) {
      try {
        const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
        const deps = { ...pkg.dependencies, ...pkg.devDependencies };
        if (deps["vite-plus"] || deps["@voidzero-dev/vite-plus-core"]) {
          return true;
        }
      } catch {
      }
    }
    for (const cfg of VITE_CONFIGS) {
      const cfgPath = path.join(dir, cfg);
      if (!fs.existsSync(cfgPath))
        continue;
      try {
        const content = fs.readFileSync(cfgPath, "utf-8");
        if (content.includes("vite-plus"))
          return true;
      } catch {
      }
    }
  }
  return false;
}
var OXLINT_CONFIGS = [
  ".oxlintrc.json",
  ".oxlintrc.jsonc",
  "oxlint.json",
  "oxlint.config.ts",
  "oxlint.config.mts"
];
function hasOxlintConfig(cwd) {
  for (const dir of walkUpDirs(cwd)) {
    for (const cfg of OXLINT_CONFIGS) {
      if (fs.existsSync(path.join(dir, cfg)))
        return true;
    }
  }
  return hasVitePlusConfig(cwd);
}
function getPreferredJstsLintRunners(context) {
  if (context.hasEslintConfig)
    return ["eslint"];
  if (context.hasOxlintConfig)
    return ["oxlint"];
  if (context.hasBiomeConfig)
    return ["biome-check-json"];
  return ["oxlint", "biome-check-json"];
}
function getJstsLintPolicy(context) {
  const hasEslint = !!context.hasEslintConfig;
  const hasOxlint = !!context.hasOxlintConfig;
  const hasBiome = !!context.hasBiomeConfig;
  return {
    hasEslintConfig: hasEslint,
    hasOxlintConfig: hasOxlint,
    hasBiomeConfig: hasBiome,
    preferredRunners: getPreferredJstsLintRunners({
      hasEslintConfig: hasEslint,
      hasOxlintConfig: hasOxlint,
      hasBiomeConfig: hasBiome
    }),
    hasExplicitNonBiomeLinter: hasEslint || hasOxlint
  };
}
function getJstsLintPolicyForCwd(cwd) {
  return getJstsLintPolicy({
    hasEslintConfig: hasEslintConfig(cwd),
    hasOxlintConfig: hasOxlintConfig(cwd),
    hasBiomeConfig: hasBiomeConfig(cwd)
  });
}

export {
  OXFMT_SUPPORTED_EXTENSIONS,
  getFormatterPolicyForFile,
  getSmartDefaultFormatterName,
  getAutoInstallToolIdForFormatter,
  shouldAutoInstallTool,
  getAutofixCapability,
  getToolCommandSpec,
  getLinterPolicyForCwd,
  hasNearestPackageJsonDependency,
  hasNearestPackageJsonField,
  hasEslintConfig,
  hasBiomeConfig,
  hasOxfmtConfig,
  hasOxfmtSvelteConfig,
  hasStylelintConfig,
  hasSqlfluffConfig,
  hasRubocopConfig,
  hasMypyConfig,
  hasYamllintConfig,
  hasMarkdownlintConfig,
  hasPrettierConfig,
  hasBlackConfig,
  hasRuffConfig,
  markdownlintConfigArgs,
  ruffConfigArgs,
  biomeConfigArgs,
  hasGolangciConfig,
  hasClangFormatConfig,
  hasPhpCsFixerConfig,
  hasStyluaConfig,
  hasOcamlformatConfig,
  hasGoogleJavaFormatConfig,
  hasCljfmtConfig,
  hasCmakeFormatConfig,
  findPSScriptAnalyzerConfigPath,
  hasPSScriptAnalyzerConfig,
  hasCsharpierConfig,
  hasOrmoluConfig,
  hasTaploConfig,
  hasTerraformConfig,
  hasSwiftformatConfig,
  hasFantomasConfig,
  hasMixFormatConfig,
  hasPhpstanConfig,
  hasDetektConfig,
  stripGradleCommentsAndStrings,
  gradleBlockRanges,
  hasKtlintConfig,
  hasGradleKtlintPlugin,
  hasKtfmtConfig,
  hasJavaBuildDescriptor,
  findCompiledClassesDir,
  hasStandardrbConfig,
  getRubocopCommand,
  hasVitePlusConfig,
  getJstsLintPolicyForCwd
};