UNPKG

pi-lens

Version:

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

2,318 lines • 77.2 kB
import { createRequire as __pilensCreateRequire } from "node:module"; const require = __pilensCreateRequire(import.meta.url);
import {
  CANONICAL_GLOBAL_CONFIG_FILE,
  CANONICAL_PROJECT_CONFIG_FILE,
  FRESHNESS_CADENCE_MS,
  GLOBAL_CONFIG_LOCATIONS,
  LEGACY_ROOT_LSP_KEYS,
  LSP_NAMESPACE_KEY,
  PROJECT_CONFIG_BASENAMES,
  PROJECT_CONFIG_LOCATIONS,
  configSearchDirs,
  dirMtimesStillFresh,
  errorClassName,
  findPiLensConfigMarkerInDir,
  getPiLensGlobalConfigPath,
  isResolvedGlobalConfigPath
} from "./chunk-VNMT7C64.js";
import {
  DEGRADATION_ENTRIES_PER_KIND,
  GLOBAL_NON_FLAG_CONFIG_SECTIONS,
  GLOBAL_ONLY_NON_FLAG_KEYS,
  LENS_FLAGS,
  LENS_TOOL_NAMES,
  PROJECT_FOREIGN_CONFIG_NAMESPACES,
  PROJECT_FOREIGN_NAMESPACE_HONORED_KEYS,
  PROJECT_NON_FLAG_CONFIG_SECTIONS,
  PROJECT_SCOPED_LENS_FLAGS,
  assignFlagConfigSection,
  flagConfigSectionKeys,
  hasFlagConfigPath,
  readFlagConfigValue,
  readToolConfig,
  recordDegradationOnce
} from "./chunk-N3YQJI6O.js";
import {
  logExtension
} from "./chunk-O6TQT6RI.js";
import {
  CONFIG_SCHEMA_ANCHOR_KEY,
  CONFIG_SCHEMA_ID,
  DEPRECATED_CONFIG_SURFACES,
  STABILITY_TIER_KEY,
  withConfigDiagnosticCode
} from "./chunk-5THXD6X5.js";
import {
  redactSecrets,
  toPositiveFinite
} from "./chunk-UK3CMEAL.js";
import {
  BoundedLruCache
} from "./chunk-ABBOA7UD.js";
import {
  homeRelativePath,
  isAtOrAboveHomeDir,
  isWindowsPath,
  walkUpDirs
} from "./chunk-Q5U6FMDI.js";

// dist/clients/generated-artifacts.js
import * as fs from "node:fs";
import * as path from "node:path";
var DEFAULT_HEADER_BYTES = 4096;
var GENERATED_DIR_NAMES = /* @__PURE__ */ new Set([
  "generated",
  "__generated__",
  "codegen",
  "__codegen__",
  "generated-sources",
  "generated_sources",
  "dist",
  "openapi-generated",
  ".openapi-generator"
]);
var GENERATED_HEADER_PATTERNS = [
  /@generated\b/i,
  /@ts-nocheck\b/i,
  /\bcode generated\b/i,
  /\bdo not edit\b/i,
  /\bautomatically generated\b/i,
  /\bthis file was generated\b/i,
  /\bgenerated by protoc\b/i,
  /\bgenerated by graphql-code-generator\b/i,
  /\bgenerated by openapi-generator\b/i,
  /\bgenerated by swagger codegen\b/i
];
var LOCKFILE_NAMES = /* @__PURE__ */ new Set([
  "package-lock.json",
  "packages.lock.json",
  "npm-shrinkwrap.json",
  "pnpm-lock.yaml",
  "yarn.lock",
  "bun.lock",
  "cargo.lock",
  "composer.lock",
  "gemfile.lock",
  "poetry.lock",
  "uv.lock",
  "go.sum"
]);
var GENERATED_FILE_PATTERNS = [
  /(^|[._-])generated([._-]|$)/i,
  /(^|[._-])gen([._-])/i,
  /_generated\.(go|rs|py|rb|java|kt|ts|tsx|js|jsx|mjs|cjs)$/i,
  /_gen\.(go|rs|py|rb|java|kt|ts|tsx|js|jsx|mjs|cjs)$/i,
  /\.pb\.(go|ts|tsx|js|jsx|mjs|cjs)$/i,
  /_pb2(_grpc)?\.py$/i,
  /\.sql\.(go|ts|js)$/i,
  /_sqlc\.go$/i,
  /\.g\.([jt]sx?|mjs|cjs|rs|go)$/i
];
var MINIFIED_BUNDLE_FILE_PATTERNS = [
  /\.min\.(js|mjs|cjs|css)$/i,
  /\.(bundle|chunk)\.(js|mjs|cjs|css)$/i
];
var DECLARATION_FILE_PATTERNS = [/\.d\.ts$/i, /\.d\.mts$/i, /\.d\.cts$/i];
function pathSegments(filePath) {
  return path.normalize(filePath).split(/[\\/]+/).filter(Boolean);
}
function basenameForShape(filePath) {
  return isWindowsPath(filePath) ? path.win32.basename(filePath) : path.basename(filePath);
}
function isGeneratedArtifactDirectoryName(dirName) {
  return GENERATED_DIR_NAMES.has(dirName.trim().toLowerCase());
}
function isDeclarationFile(filePath) {
  const base = basenameForShape(filePath).toLowerCase();
  return DECLARATION_FILE_PATTERNS.some((pattern) => pattern.test(base));
}
function hasStrongGeneratedArtifactPath(filePath) {
  const segments = pathSegments(filePath);
  const dirSegments = segments.slice(0, -1);
  if (dirSegments.some((segment) => isGeneratedArtifactDirectoryName(segment))) {
    return true;
  }
  const base = basenameForShape(filePath);
  if (LOCKFILE_NAMES.has(base.toLowerCase()))
    return true;
  return MINIFIED_BUNDLE_FILE_PATTERNS.some((pattern) => pattern.test(base));
}
function hasWeakGeneratedFileNamePattern(filePath) {
  const base = basenameForShape(filePath);
  return GENERATED_FILE_PATTERNS.some((pattern) => pattern.test(base));
}
function hasGeneratedArtifactContent(content) {
  const header = content.slice(0, DEFAULT_HEADER_BYTES);
  return GENERATED_HEADER_PATTERNS.some((pattern) => pattern.test(header));
}
var MACHINE_EMITTED_LINE_SHAPE_MIN_CONTENT = 2048;
var MACHINE_EMITTED_LINE_SHAPE_MEAN_THRESHOLD = 2500;
function classifyMachineEmittedLineShape(content) {
  const contentLength = content.length;
  if (contentLength < MACHINE_EMITTED_LINE_SHAPE_MIN_CONTENT) {
    return { generated: false, meanLineLength: 0 };
  }
  let nonEmptyLineTotal = 0;
  let nonEmptyLineCount = 0;
  let currentLineLength = 0;
  for (let i = 0; i < contentLength; i++) {
    const code = content.charCodeAt(i);
    if (code === 10) {
      if (currentLineLength > 0) {
        nonEmptyLineTotal += currentLineLength;
        nonEmptyLineCount += 1;
      }
      currentLineLength = 0;
    } else if (code !== 13) {
      currentLineLength += 1;
    }
  }
  if (currentLineLength > 0) {
    nonEmptyLineTotal += currentLineLength;
    nonEmptyLineCount += 1;
  }
  if (nonEmptyLineCount === 0)
    return { generated: false, meanLineLength: 0 };
  const meanLineLength = nonEmptyLineTotal / nonEmptyLineCount;
  return {
    generated: meanLineLength > MACHINE_EMITTED_LINE_SHAPE_MEAN_THRESHOLD,
    meanLineLength
  };
}
var HEADER_VERDICT_MEMO_CAP = 5e4;
var headerAnalysisMemo = new BoundedLruCache(HEADER_VERDICT_MEMO_CAP);
function readFileHeader(filePath, maxBytes = DEFAULT_HEADER_BYTES) {
  let fd;
  try {
    fd = fs.openSync(filePath, "r");
    const buffer = Buffer.alloc(Math.max(0, maxBytes));
    const bytesRead = fs.readSync(fd, buffer, 0, buffer.length, 0);
    return buffer.toString("utf8", 0, bytesRead);
  } catch {
    return void 0;
  } finally {
    if (fd !== void 0) {
      try {
        fs.closeSync(fd);
      } catch {
      }
    }
  }
}
function analyzeFileHeader(filePath, maxBytes) {
  let key;
  try {
    const st = fs.statSync(filePath);
    key = `${st.mtimeMs}:${st.size}:${maxBytes}:${filePath}`;
  } catch {
    return void 0;
  }
  const cached = headerAnalysisMemo.get(key);
  if (cached !== void 0)
    return cached;
  const header = readFileHeader(filePath, maxBytes);
  const analysis = header === void 0 ? { banner: false, shape: { generated: false, meanLineLength: 0 } } : {
    banner: hasGeneratedArtifactContent(header),
    shape: classifyMachineEmittedLineShape(header)
  };
  headerAnalysisMemo.set(key, analysis);
  return analysis;
}
function classifyGeneratedOrArtifactDetailed(filePath, options = {}) {
  if (hasStrongGeneratedArtifactPath(filePath)) {
    return { verdict: "generated", evidence: "strong" };
  }
  if (options.includeDeclarations && isDeclarationFile(filePath)) {
    return { verdict: "generated", evidence: "strong" };
  }
  const weakNameMatch = hasWeakGeneratedFileNamePattern(filePath);
  if (options.content !== void 0) {
    if (hasGeneratedArtifactContent(options.content)) {
      return { verdict: "generated", evidence: "content" };
    }
    const shape = classifyMachineEmittedLineShape(options.content);
    if (shape.generated) {
      return {
        verdict: "generated",
        evidence: "line-shape",
        lineShapeMean: shape.meanLineLength
      };
    }
    return { verdict: weakNameMatch ? "override" : "clean" };
  }
  if (options.readContentHeader) {
    const analysis = analyzeFileHeader(filePath, options.maxHeaderBytes ?? DEFAULT_HEADER_BYTES);
    if (analysis?.banner) {
      return { verdict: "generated", evidence: "content" };
    }
    if (analysis?.shape.generated) {
      return {
        verdict: "generated",
        evidence: "line-shape",
        lineShapeMean: analysis.shape.meanLineLength
      };
    }
    return { verdict: weakNameMatch ? "override" : "clean" };
  }
  return weakNameMatch ? { verdict: "generated", evidence: "name-only" } : { verdict: "clean" };
}
function classifyGeneratedOrArtifact(filePath, options = {}) {
  return classifyGeneratedOrArtifactDetailed(filePath, options).verdict;
}
function isGeneratedOrArtifact(filePath, options = {}) {
  return classifyGeneratedOrArtifact(filePath, options) === "generated";
}

// dist/clients/file-role.js
import { basename as basename2, dirname, win32 as win322 } from "node:path";
var RE_EXPORT_LINE = /^export\s+(type\s+)?\*\s+from\s+|^export\s*\{[^}]*\}\s+from\s+/;
function isReExportBarrel(content) {
  const lines = content.split(/\r?\n/).map((l) => l.trim()).filter((l) => l && !l.startsWith("//") && !l.startsWith("*") && !l.startsWith("/*"));
  return lines.length > 0 && lines.every((l) => RE_EXPORT_LINE.test(l));
}
function isTypeStub(content) {
  const lines = content.split(/\r?\n/).map((l) => l.trim()).filter((l) => l && !l.startsWith("//") && !l.startsWith("*") && !l.startsWith("/*") && l !== "{" && l !== "}");
  if (lines.length < 4)
    return false;
  const typeOnlyLines = lines.filter((l) => /^(export\s+)?(interface|type|abstract\s+class|declare\s+(class|function|const|var|let|module|namespace|enum))\s+/.test(l) || /^(import\s+type\s+|import\s+\{)/.test(l) || /^export\s+type\s+\{/.test(l));
  return typeOnlyLines.length / lines.length >= 0.75;
}
function testFileNameMatches(rawBase, base) {
  return base.includes(".test.") || base.includes(".spec.") || base.includes("_test.") || base.includes("_spec.") || base.startsWith("test_") || base.startsWith("spec_") || /(Test|Tests|TestCase)\.(java|kt|kts|cs|fs|php)$/.test(rawBase);
}
function isTestFileName(filePath) {
  const windowsShaped = isWindowsPath(filePath);
  const rawBase = windowsShaped ? win322.basename(filePath) : basename2(filePath);
  return testFileNameMatches(rawBase, rawBase.toLowerCase());
}
function detectFileRole(filePath, content) {
  const windowsShaped = isWindowsPath(filePath);
  const rawBase = windowsShaped ? win322.basename(filePath) : basename2(filePath);
  const base = rawBase.toLowerCase();
  const dir = (windowsShaped ? win322.dirname(filePath) : dirname(filePath)).replace(/\\/g, "/").toLowerCase();
  if (testFileNameMatches(rawBase, base) || /(^|[\\/])__tests__([\\/]|$)/.test(dir) || dir.includes("/test/") || dir.includes("/tests/") || dir.includes("/spec/") || dir.includes("/specs/") || /[/_-]tests?\/?$/.test(dir) || /[/_-]specs?\/?$/.test(dir))
    return "test";
  if (isGeneratedOrArtifact(filePath, { content, includeDeclarations: false })) {
    return "generated";
  }
  const forwardDir = dir.replace(/\\/g, "/");
  if (forwardDir.includes("/migrations/") || forwardDir.includes("/migration/") || /\/\d{4,}_[a-z]/.test(forwardDir + "/" + base))
    return "migration";
  if (base.endsWith(".config.ts") || base.endsWith(".config.js") || base.endsWith(".config.mjs") || base.endsWith(".config.cjs") || base === "vite.config.ts" || base === "jest.config.ts" || base === "jest.config.js" || base === "webpack.config.js" || base === "rollup.config.js" || base === "tailwind.config.js" || base === "tailwind.config.ts" || base === "next.config.js" || base === "next.config.mjs")
    return "config";
  if (base.endsWith(".d.ts"))
    return "stub";
  if (content && isTypeStub(content))
    return "stub";
  const isIndexFile = base === "index.ts" || base === "index.tsx" || base === "index.js" || base === "index.mjs" || base === "index.cjs" || base === "index.jsx" || base === "__init__.py" || base === "mod.rs";
  if (isIndexFile) {
    if (content && isReExportBarrel(content))
      return "re-export";
    return "init";
  }
  return "source";
}

// dist/clients/user-notify.js
var notifierGetter;
function notifyUserDegradation(message, level = "warning", options) {
  try {
    const notify = notifierGetter?.();
    const coded = options?.code ? withConfigDiagnosticCode(message, options.code) : message;
    notify?.(coded, level);
  } catch {
  }
}

// dist/clients/config-warn.js
var SUBSYSTEM_CONFIG_LABEL = {
  "lsp-config": "LSP config",
  "lens-config": "global config",
  "project-lens-config": "project config"
};
var IGNORED_CONFIG_CODE = "PILENS_CFG_0001";
var IGNORED_CONFIG_KIND = "config-ignored";
var DEPRECATED_CONFIG_KIND = "config-deprecated";
var SUPPRESSED_NOTICE_CODE = "PILENS_CFG_0007";
var SUPPRESSED_NOTICE_KIND = "config-notice-suppressed";
var DEPRECATION_NOUN_BY_CODE = new Map(DEPRECATED_CONFIG_SURFACES.map((row) => [
  row.code,
  row.kind === "file" ? "location" : "key"
]));
var warnedIgnoredConfigs = /* @__PURE__ */ new Set();
var JSON_PARSE_POSITION = /at position (\d+) \(line (\d+) column (\d+)\)/;
function isSyntaxError(error) {
  return typeof error === "object" && error !== null && error.name === "SyntaxError" && typeof error.message === "string";
}
function lineColAt(sourceText, offset) {
  let line = 1;
  let lastNewline = -1;
  for (let i = 0; i < offset; i++) {
    if (sourceText[i] === "\n") {
      line++;
      lastNewline = i;
    }
  }
  return { line, col: offset - lastNewline };
}
function parseUnexpectedTokenMessage(message) {
  const prefix = "Unexpected token '";
  if (!message.startsWith(prefix))
    return void 0;
  const closeIdx = message.indexOf("', ", prefix.length);
  const token = message.slice(prefix.length, closeIdx);
  let rest = message.slice(closeIdx + 3);
  if (rest.startsWith("..."))
    rest = rest.slice(3);
  if (!rest.startsWith('"'))
    return void 0;
  rest = rest.slice(1);
  const truncatedSuffix = '"... is not valid JSON';
  const plainSuffix = '" is not valid JSON';
  const snippet = rest.endsWith(truncatedSuffix) ? rest.slice(0, -truncatedSuffix.length) : rest.endsWith(plainSuffix) ? rest.slice(0, -plainSuffix.length) : void 0;
  return snippet === void 0 ? void 0 : { token, snippet };
}
function locateSyntaxErrorOffset(message, sourceText) {
  const stated = JSON_PARSE_POSITION.exec(message);
  if (stated)
    return Number(stated[1]);
  const parsed = parseUnexpectedTokenMessage(message);
  if (!parsed)
    return void 0;
  const { token, snippet } = parsed;
  const snippetAt = sourceText.indexOf(snippet);
  if (snippetAt === -1 || sourceText.indexOf(snippet, snippetAt + 1) !== -1) {
    return void 0;
  }
  const tokenAt = snippet.indexOf(token);
  const onlyTokenOccurrence = tokenAt !== -1 && snippet.indexOf(token, tokenAt + 1) === -1;
  return snippetAt + (onlyTokenOccurrence ? tokenAt : 0);
}
function normalizeParseErrorReason(error, options = {}) {
  if (options.classOnly) {
    return errorClassName(error);
  }
  if (isSyntaxError(error)) {
    if (options.sourceText !== void 0) {
      const offset = locateSyntaxErrorOffset(error.message, options.sourceText);
      if (offset !== void 0) {
        const { line, col } = lineColAt(options.sourceText, offset);
        return `${error.name} at line ${line} col ${col}`;
      }
      return error.name;
    }
    const match = JSON_PARSE_POSITION.exec(error.message);
    return match ? `${error.name} at line ${match[2]} col ${match[3]}` : error.name;
  }
  const message = error instanceof Error ? error.message : String(error);
  return redactSecrets(message);
}
function degradationKindFor(suppressedNotice, deprecationNoun) {
  if (suppressedNotice)
    return SUPPRESSED_NOTICE_KIND;
  return deprecationNoun ? DEPRECATED_CONFIG_KIND : IGNORED_CONFIG_KIND;
}
function noticeMessage(options) {
  const label = SUBSYSTEM_CONFIG_LABEL[options.subsystem];
  if (options.suppressedNotice) {
    return `${label} notices for ${options.file} were summarised: ${options.reason}`;
  }
  return options.deprecationNoun ? `deprecated ${label} ${options.deprecationNoun} in ${options.file}: ${options.reason}` : `ignoring invalid ${label} ${options.file}: ${options.reason}`;
}
function warnIgnoredConfigOnce(options) {
  const { subsystem, file, key } = options;
  const code = options.code ?? IGNORED_CONFIG_CODE;
  const reason = redactSecrets(typeof options.reason === "string" ? options.reason : normalizeParseErrorReason(options.reason.parseError, {
    sourceText: options.reason.sourceText
  }));
  const suppressedNotice = code === SUPPRESSED_NOTICE_CODE;
  const deprecationNoun = suppressedNotice ? void 0 : DEPRECATION_NOUN_BY_CODE.get(code);
  recordDegradationOnce({
    kind: degradationKindFor(suppressedNotice, deprecationNoun),
    subject: key ? `${file}\0${key}` : file,
    reason,
    metadata: { subsystem, configPath: file },
    code
  });
  const latchKey = `${subsystem}\0${file}\0${key ?? ""}\0${reason}`;
  if (warnedIgnoredConfigs.has(latchKey))
    return;
  warnedIgnoredConfigs.add(latchKey);
  const message = noticeMessage({
    subsystem,
    file,
    reason,
    suppressedNotice,
    deprecationNoun
  });
  logExtension({
    subsystem,
    level: "warn",
    message: withConfigDiagnosticCode(message, code),
    metadata: { configPath: file, reason, code, ...key ? { key } : {} }
  });
  notifyUserDegradation(`pi-lens: ${message}`, "warning", { code });
}

// dist/clients/project-lens-config.js
import * as fs3 from "node:fs";
import * as os2 from "node:os";
import * as path3 from "node:path";

// dist/clients/config-resolve.js
import * as fs2 from "node:fs";
import * as os from "node:os";
import * as path2 from "node:path";

// dist/clients/config-core/provenance.js
var SOURCE_TIERS = [
  "builtin",
  "global",
  "project",
  "nested-project",
  "env",
  "cli",
  "host"
];
var TIER_CLASS = {
  builtin: "default",
  global: "operator",
  project: "repo",
  "nested-project": "repo",
  env: "operator",
  cli: "operator",
  host: "operator"
};
function tierPrecedence(tier) {
  return SOURCE_TIERS.indexOf(tier);
}
function isOperatorTier(tier) {
  return TIER_CLASS[tier] === "operator";
}
function provenanceView(resolved, homeDir) {
  const entries = [...resolved.provenance.values()].map((entry) => ({
    key: entry.key,
    tier: entry.tier,
    ...entry.file === void 0 ? {} : { file: homeRelativePath(entry.file, homeDir) },
    ...entry.trust === void 0 ? {} : { trust: entry.trust }
  })).sort((left, right) => compareKeys(left.key, right.key));
  return { entries };
}
function provenanceFor(resolved, pointer) {
  let candidate = pointer;
  for (; ; ) {
    const entry = resolved.provenance.get(candidate);
    if (entry)
      return entry;
    if (candidate === "")
      return void 0;
    const cut = candidate.lastIndexOf("/");
    candidate = cut <= 0 ? "" : candidate.slice(0, cut);
  }
}
function compareKeys(left, right) {
  if (left < right)
    return -1;
  return left > right ? 1 : 0;
}

// dist/clients/config-core/deny.js
function provenanceOfContribution(source, key) {
  if (!source)
    return void 0;
  return {
    tier: source.tier,
    key,
    ...source.file === void 0 ? {} : { file: source.file },
    ...source.trust === void 0 ? {} : { trust: source.trust }
  };
}
function denyProvenance(contributions, resolution, key) {
  return provenanceOfContribution(contributions[resolution.winner], key);
}
function denyMemberProvenance(contributions, resolution, key) {
  const entries = [];
  resolution.memberWinners.forEach((index, position) => {
    const pointer = `${key}/${position}`;
    const entry = provenanceOfContribution(contributions[index], pointer);
    if (entry)
      entries.push(entry);
  });
  return entries;
}
var EMPTY_MEMBER_WINNERS = Object.freeze([]);
function byPrecedence(contributions) {
  return contributions.map((contribution, index) => ({ contribution, index })).sort((left, right) => tierPrecedence(left.contribution.tier) - tierPrecedence(right.contribution.tier));
}
function resolveBooleanDeny(contributions) {
  const ordered = byPrecedence(contributions);
  if (ordered.length === 0)
    return {
      value: void 0,
      winner: -1,
      denied: false,
      memberWinners: EMPTY_MEMBER_WINNERS
    };
  const denials = ordered.filter((entry) => entry.contribution.value === false);
  if (denials.length === 0) {
    const last = ordered[ordered.length - 1];
    return {
      value: last.contribution.value,
      winner: last.index,
      denied: false,
      memberWinners: EMPTY_MEMBER_WINNERS
    };
  }
  const operatorDenial = denials.find((entry) => isOperatorTier(entry.contribution.tier));
  if (operatorDenial) {
    return {
      value: false,
      winner: operatorDenial.index,
      denied: true,
      memberWinners: EMPTY_MEMBER_WINNERS
    };
  }
  const firstDenial = denials[0];
  const lift = ordered.find((entry) => entry.contribution.value === true && isOperatorTier(entry.contribution.tier) && tierPrecedence(entry.contribution.tier) > tierPrecedence(firstDenial.contribution.tier));
  if (lift) {
    return {
      value: true,
      winner: lift.index,
      denied: false,
      memberWinners: EMPTY_MEMBER_WINNERS
    };
  }
  return {
    value: false,
    winner: firstDenial.index,
    denied: true,
    memberWinners: EMPTY_MEMBER_WINNERS
  };
}
function resolveArrayDeny(contributions) {
  const ordered = byPrecedence(contributions);
  const seen = /* @__PURE__ */ new Set();
  const members = [];
  const memberWinners = [];
  let winner = -1;
  for (const entry of ordered) {
    const value = entry.contribution.value;
    if (!Array.isArray(value))
      continue;
    for (const member of value) {
      const identity = primitiveIdentity(member);
      if (identity !== void 0) {
        if (seen.has(identity))
          continue;
        seen.add(identity);
      }
      members.push(member);
      memberWinners.push(entry.index);
      if (winner === -1)
        winner = entry.index;
    }
  }
  if (winner === -1) {
    const last = ordered[ordered.length - 1];
    return {
      value: members,
      winner: last ? last.index : -1,
      denied: false,
      memberWinners
    };
  }
  return { value: members, winner, denied: members.length > 0, memberWinners };
}
function primitiveIdentity(member) {
  if (typeof member === "string")
    return `s:${member}`;
  if (typeof member === "number" || typeof member === "boolean") {
    return `p:${String(member)}`;
  }
  if (member === null)
    return "null";
  return void 0;
}

// dist/clients/config-core/safe-object.js
var MAX_CONFIG_DEPTH = 32;
var UNSAFE_CONFIG_KEYS = [
  "__proto__",
  "constructor",
  "prototype"
];
var UNSAFE_KEY_SET = new Set(UNSAFE_CONFIG_KEYS);
function isUnsafeConfigKey(name) {
  return UNSAFE_KEY_SET.has(name);
}
var UNSAFE_KEY_REASON = "config key would modify the object prototype; ignored";
function safeAssign(target, name, value) {
  if (isUnsafeConfigKey(name))
    return false;
  Object.defineProperty(target, name, {
    value,
    writable: true,
    enumerable: true,
    configurable: true
  });
  return true;
}

// dist/clients/config-core/records.js
var MAX_MIGRATION_RECORDS = DEGRADATION_ENTRIES_PER_KIND;
var MAX_RECORD_KEY_LENGTH = 120;
var UNBOUNDED_RECORD_CODES = /* @__PURE__ */ new Set(["PILENS_CFG_0008"]);
var SUBJECT_SEPARATOR = String.fromCharCode(0);
function migrationSubject(file, key) {
  return key.length > 0 ? `${file}${SUBJECT_SEPARATOR}${key}` : file;
}
function stripControlCharacters(text) {
  let out = "";
  for (const character of text) {
    const code = character.codePointAt(0) ?? 0;
    out += code < 32 || code === 127 ? " " : character;
  }
  return out;
}
function boundedKeyLabel(key) {
  const printable = stripControlCharacters(key).trim();
  const bounded = printable.length > MAX_RECORD_KEY_LENGTH ? `${printable.slice(0, MAX_RECORD_KEY_LENGTH)}...` : printable;
  return redactSecrets(bounded);
}
function jsonTypeName(value) {
  if (value === null)
    return "null";
  if (Array.isArray(value))
    return "array";
  return typeof value;
}
var UNANCHORED_RECORD_LABEL = "(config resolution)";
var MigrationRecordCollector = class {
  kept = [];
  dropped;
  limit;
  /**
   * `priorDropped` SEEDS the drop count with records an EARLIER bound already
   * discarded before this collector saw the list (#2426 review round 6, F1).
   *
   * The shared resolution is bounded twice by construction: `resolveConfig`
   * runs its own collector across every source, and the loader then finalizes
   * what survived plus the deprecation records it composes itself. Only the
   * second bound used to be counted, so a `.pi-lens.json` whose 40 refused
   * keys were truncated to 19 told the user ONE notice had been suppressed
   * when 21 had. A suppression count that undercounts is worse than no count:
   * it tells the user the list they were shown is almost complete.
   */
  constructor(limit = MAX_MIGRATION_RECORDS, priorDropped = 0) {
    this.limit = Math.max(0, Math.trunc(limit));
    this.dropped = Math.max(0, Math.trunc(priorDropped));
  }
  add(record4) {
    if (this.kept.length >= this.limit) {
      if (UNBOUNDED_RECORD_CODES.has(record4.code)) {
        this.kept.push(record4);
        return;
      }
      this.dropped += 1;
      return;
    }
    this.kept.push(record4);
  }
  /** The retained records, oldest first. */
  get records() {
    return this.kept;
  }
  /** How many records the bound discarded. Counted, never silently zero. */
  get droppedCount() {
    return this.dropped;
  }
  /**
   * The retained records plus, when the bound discarded any, ONE record
   * counting them.
   *
   * The whole of "bound a record list and say so when it truncates" lives
   * here, and nowhere else (#2426 review round 5, F-A/F-B/S-A). Three
   * producers had grown their own copy of it — the shared resolution, the
   * project loader's unknown-key scan, and (by omission, which is how the
   * defect showed) the project loader's legacy-document enumeration, which
   * shipped its records raw and let one file's key count set the
   * notification count. A fourth producer must not have to remember any of
   * this: it calls `finalizeRecords` and is bounded.
   *
   * Two finalized lists that anchor to the SAME file and suppress the SAME
   * number of records render one notice, not two. That is the warn-once
   * latch doing its documented job: both records state the identical fact
   * about the identical file, and the user has one thing to do about it.
   */
  finalize(anchor) {
    if (this.dropped === 0)
      return this.kept;
    const file = anchor?.file ?? UNANCHORED_RECORD_LABEL;
    return [
      ...this.kept,
      {
        code: "PILENS_CFG_0007",
        file,
        key: "",
        subject: migrationSubject(file, ""),
        reason: `${this.dropped} further config notices were suppressed by the bound of ${MAX_MIGRATION_RECORDS}`,
        ...anchor?.tier === void 0 ? {} : { tier: anchor.tier }
      }
    ];
  }
};
function finalizeRecords(records, anchor, priorDropped = 0) {
  const collector = new MigrationRecordCollector(MAX_MIGRATION_RECORDS - 1, priorDropped);
  for (const record4 of records)
    collector.add(record4);
  return collector.finalize(anchor);
}

// dist/clients/config-core/schema.js
function isConfigObject(value) {
  return typeof value === "object" && value !== null && !Array.isArray(value);
}
var MERGE_STRATEGY_KEY = "x-merge-strategy";
var DEFAULT_MERGE_STRATEGY = "replace";
var KEYED_PREFIX = "keyed:";
function isMergeStrategy(value) {
  if (typeof value !== "string")
    return false;
  if (value === "replace" || value === "append")
    return true;
  return value.startsWith(KEYED_PREFIX) && value.length > KEYED_PREFIX.length;
}
function mergeStrategyOf(node) {
  const declared = node?.[MERGE_STRATEGY_KEY];
  return isMergeStrategy(declared) ? declared : DEFAULT_MERGE_STRATEGY;
}
function keyedField(strategy) {
  return strategy.startsWith(KEYED_PREFIX) ? strategy.slice(KEYED_PREFIX.length) : void 0;
}
var DENY_KEY = "x-deny";
var DENY_POLICIES = ["boolean-false", "array-union"];
function isDenyPolicy(value) {
  return typeof value === "string" && DENY_POLICIES.includes(value);
}
function denyPolicyOf(node) {
  const declared = node?.[DENY_KEY];
  return isDenyPolicy(declared) ? declared : void 0;
}
function isSchemaNode(value) {
  return typeof value === "object" && value !== null && !Array.isArray(value);
}
function isPlainObject(value) {
  return typeof value === "object" && value !== null && !Array.isArray(value);
}
function schemaType(node) {
  const declared = node?.type;
  return typeof declared === "string" ? declared : void 0;
}
var SCHEMA_TYPES = [
  "object",
  "array",
  "string",
  "number",
  "integer",
  "boolean",
  "null"
];
var SCHEMA_TYPE_SET = new Set(SCHEMA_TYPES);
function isKnownSchemaType(declared) {
  return declared !== void 0 && SCHEMA_TYPE_SET.has(declared);
}
function propertySchema(node, name) {
  if (!node)
    return void 0;
  const properties = node.properties;
  if (isSchemaNode(properties) && Object.hasOwn(properties, name)) {
    const child = properties[name];
    if (isSchemaNode(child))
      return child;
  }
  const patterns = node.patternProperties;
  if (isSchemaNode(patterns)) {
    for (const [pattern, child] of Object.entries(patterns)) {
      if (!isSchemaNode(child))
        continue;
      let matcher;
      try {
        matcher = new RegExp(pattern);
      } catch {
        continue;
      }
      if (matcher.test(name))
        return child;
    }
  }
  return void 0;
}
function additionalPropertyPolicy(node) {
  const declared = node?.additionalProperties;
  if (declared === true)
    return { kind: "keep" };
  if (isSchemaNode(declared))
    return { kind: "validate", schema: declared };
  return { kind: "drop" };
}
function itemsSchema(node) {
  const declared = node?.items;
  return isSchemaNode(declared) ? declared : void 0;
}

// dist/clients/config-core/merge.js
function merge(sources, schema, options = {}) {
  const ordered = [...sources].map((source, order) => ({ source, order })).sort((left, right) => {
    const byTier = tierPrecedence(left.source.tier) - tierPrecedence(right.source.tier);
    return byTier !== 0 ? byTier : left.order - right.order;
  }).map((entry, rank) => ({ source: entry.source, rank }));
  const contributions = [];
  for (const entry of ordered) {
    const value2 = entry.source.value;
    if (value2 === void 0)
      continue;
    contributions.push({ source: entry.source, value: value2, rank: entry.rank });
  }
  const provenance = /* @__PURE__ */ new Map();
  const context = {
    provenance,
    collector: options.collector ?? new MigrationRecordCollector(),
    file: options.file ?? ""
  };
  const value = mergeNode(contributions, schema, "", context, 0);
  return { value, provenance };
}
function record(context, entry) {
  const key = boundedKeyLabel(entry.key);
  context.collector.add({
    code: entry.code,
    file: context.file,
    key,
    subject: migrationSubject(context.file, key),
    reason: entry.reason
  });
}
function provenanceOf(contribution, key) {
  const { source } = contribution;
  return {
    tier: source.tier,
    key,
    ...source.file === void 0 ? {} : { file: source.file },
    ...source.trust === void 0 ? {} : { trust: source.trust }
  };
}
function stamp(context, contribution, key) {
  context.provenance.set(key, provenanceOf(contribution, key));
}
function mergeNode(contributions, schema, key, context, depth) {
  if (contributions.length === 0)
    return void 0;
  if (depth > MAX_CONFIG_DEPTH) {
    record(context, {
      code: "PILENS_CFG_0005",
      key,
      reason: `config nesting exceeds ${MAX_CONFIG_DEPTH} levels; ignored`
    });
    return void 0;
  }
  const denyPolicy = denyPolicyOf(schema);
  if (denyPolicy)
    return mergeDeny(contributions, denyPolicy, key, context);
  if (isObjectNode(schema, contributions)) {
    return mergeObject(contributions, schema, key, context, depth);
  }
  if (isArrayNode(schema, contributions)) {
    return mergeArray(contributions, schema, key, context, depth);
  }
  const winner = contributions[contributions.length - 1];
  stamp(context, winner, key);
  return winner.value;
}
function isObjectNode(schema, contributions) {
  const declared = schemaType(schema);
  if (declared === "object")
    return true;
  if (isKnownSchemaType(declared))
    return false;
  if (schema && isSchemaNode(schema.properties))
    return true;
  return contributions.every((entry) => isPlainObject(entry.value));
}
function isArrayNode(schema, contributions) {
  const declared = schemaType(schema);
  if (declared === "array")
    return true;
  if (isKnownSchemaType(declared))
    return false;
  return contributions.every((entry) => Array.isArray(entry.value));
}
function mergeDeny(contributions, policy, key, context) {
  const denyContributions = contributions.map((entry2) => ({
    tier: entry2.source.tier,
    value: entry2.value,
    ...entry2.source.file === void 0 ? {} : { file: entry2.source.file },
    ...entry2.source.trust === void 0 ? {} : { trust: entry2.source.trust }
  }));
  const resolution = policy === "boolean-false" ? resolveBooleanDeny(denyContributions) : resolveArrayDeny(denyContributions);
  const entry = denyProvenance(denyContributions, resolution, key);
  if (entry)
    context.provenance.set(key, entry);
  for (const member of denyMemberProvenance(denyContributions, resolution, key)) {
    context.provenance.set(member.key, member);
  }
  return resolution.value;
}
function mergeObject(contributions, schema, key, context, depth) {
  const objects = contributions.filter((entry) => isConfigObject(entry.value));
  if (objects.length === 0) {
    const winner = contributions[contributions.length - 1];
    stamp(context, winner, key);
    return winner.value;
  }
  const names = [];
  for (const entry of objects) {
    for (const name of Object.keys(entry.value)) {
      if (names.includes(name))
        continue;
      if (isUnsafeConfigKey(name)) {
        record(context, {
          code: "PILENS_CFG_0006",
          key: `${key}/${name}`,
          reason: UNSAFE_KEY_REASON
        });
        continue;
      }
      names.push(name);
    }
  }
  const out = {};
  for (const name of names) {
    const childKey = `${key}/${name}`;
    const childContributions = [];
    for (const entry of objects) {
      const parent = entry.value;
      if (!Object.hasOwn(parent, name))
        continue;
      childContributions.push({
        source: entry.source,
        value: parent[name],
        rank: entry.rank
      });
    }
    const merged = mergeNode(childContributions, propertySchema(schema, name), childKey, context, depth + 1);
    if (merged !== void 0)
      safeAssign(out, name, merged);
  }
  return out;
}
function mergeArray(contributions, schema, key, context, depth) {
  const arrays = contributions.filter((entry) => Array.isArray(entry.value));
  if (arrays.length === 0) {
    const winner2 = contributions[contributions.length - 1];
    stamp(context, winner2, key);
    return winner2.value;
  }
  const strategy = mergeStrategyOf(schema);
  const keyed = keyedField(strategy);
  if (keyed !== void 0) {
    return mergeKeyedArray(arrays, schema, keyed, key, context, depth);
  }
  if (strategy === "append")
    return appendArrays(arrays, key, context);
  const winner = arrays[arrays.length - 1];
  stamp(context, winner, key);
  return [...winner.value];
}
function appendArrays(arrays, key, context) {
  const out = [];
  for (const entry of arrays) {
    for (const member of entry.value) {
      stamp(context, entry, `${key}/${out.length}`);
      out.push(member);
    }
  }
  stamp(context, arrays[0], key);
  return out;
}
function mergeKeyedArray(arrays, schema, field, key, context, depth) {
  const items = itemsSchema(schema);
  const order = [];
  const groups = /* @__PURE__ */ new Map();
  const unkeyed = [];
  for (const entry of arrays) {
    for (const member of entry.value) {
      const identity = keyIdentity(member, field);
      const memberContribution = {
        source: entry.source,
        value: member,
        rank: entry.rank
      };
      if (identity === void 0) {
        unkeyed.push(memberContribution);
        continue;
      }
      const group = groups.get(identity);
      if (group) {
        group.push(memberContribution);
        continue;
      }
      order.push(identity);
      groups.set(identity, [memberContribution]);
    }
  }
  const out = [];
  const pushMerged = (group) => {
    const merged = mergeNode(group, items, `${key}/${out.length}`, context, depth + 1);
    if (merged !== void 0)
      out.push(merged);
  };
  for (const identity of order)
    pushMerged(groups.get(identity) ?? []);
  for (const entry of unkeyed)
    pushMerged([entry]);
  stamp(context, arrays[0], key);
  return out;
}
function keyIdentity(member, field) {
  if (!isConfigObject(member))
    return void 0;
  const value = member[field];
  if (typeof value === "string")
    return `s:${value}`;
  if (typeof value === "number" && Number.isFinite(value)) {
    return `n:${String(value)}`;
  }
  return void 0;
}

// dist/clients/config-core/normalize.js
var DROPPED = /* @__PURE__ */ Symbol("dropped");
var OPEN_SCHEMA = { additionalProperties: true };
function validate(raw, schema, options = {}) {
  const collector = options.collector ?? new MigrationRecordCollector();
  const file = options.file ?? "";
  const context = {
    collector,
    file,
    tier: options.tier,
    path: []
  };
  let walked;
  try {
    walked = walk(raw, schema, context, 0, /* @__PURE__ */ new Set());
  } catch (error) {
    record2(context, {
      code: "PILENS_CFG_0005",
      key: "",
      reason: `config validation failed internally (${errorClassName(error)}); document ignored`
    });
    walked = DROPPED;
  }
  return {
    value: walked === DROPPED ? void 0 : walked,
    records: collector.records,
    droppedRecordCount: collector.droppedCount
  };
}
function pointerOf(path4) {
  return path4.length === 0 ? "" : `/${path4.join("/")}`;
}
function record2(context, entry) {
  const key = boundedKeyLabel(entry.key);
  context.collector.add({
    code: entry.code,
    file: context.file,
    key,
    subject: migrationSubject(context.file, key),
    reason: entry.reason,
    tier: context.tier
  });
}
function walk(value, schema, context, depth, onPath) {
  const pointer = pointerOf(context.path);
  if (depth > MAX_CONFIG_DEPTH) {
    record2(context, {
      code: "PILENS_CFG_0005",
      key: pointer,
      reason: `config nesting exceeds ${MAX_CONFIG_DEPTH} levels; ignored`
    });
    return DROPPED;
  }
  if (typeof value === "object" && value !== null) {
    if (onPath.has(value)) {
      record2(context, {
        code: "PILENS_CFG_0005",
        key: pointer,
        reason: "config value refers to itself; ignored"
      });
      return DROPPED;
    }
  }
  const declared = schemaType(schema);
  if (declared === "object")
    return walkObject(value, schema, context, depth, onPath);
  if (declared === "array")
    return walkArray(value, schema, context, depth, onPath);
  if (isKnownSchemaType(declared)) {
    return walkScalar(value, schema, context, declared);
  }
  return walkOpaque(value, schema, context, depth, onPath);
}
function walkOpaque(value, schema, context, depth, onPath) {
  if (isPlainObject(value)) {
    const effective = claimsChildren(schema) ? schema : OPEN_SCHEMA;
    return walkObject(value, effective, context, depth, onPath);
  }
  if (Array.isArray(value)) {
    return walkArray(value, schema, context, depth, onPath);
  }
  return walkLeaf(value, schema, context);
}
function claimsChildren(schema) {
  if (!schema)
    return false;
  return isSchemaNode(schema.properties) || isSchemaNode(schema.patternProperties) || schema.additionalProperties !== void 0;
}
function walkLeaf(value, schema, context) {
  if (!isConfigScalar(value)) {
    record2(context, {
      code: "PILENS_CFG_0005",
      key: pointerOf(context.path),
      reason: `config value is not valid JSON data (${jsonTypeName(value)}); ignored`
    });
    return DROPPED;
  }
  return checkEnum(value, schema, context) ? value : DROPPED;
}
function isConfigScalar(value) {
  if (value === null)
    return true;
  const type = typeof value;
  if (type === "string" || type === "boolean")
    return true;
  return type === "number" && Number.isFinite(value);
}
function walkObject(value, schema, context, depth, onPath) {
  if (!isPlainObject(value)) {
    record2(context, {
      code: "PILENS_CFG_0005",
      key: pointerOf(context.path),
      reason: `expected an object, got ${jsonTypeName(value)}; ignored`
    });
    return DROPPED;
  }
  const nextPath = onPath.add(value);
  const policy = additionalPropertyPolicy(schema);
  const out = {};
  for (const [name, child] of Object.entries(value)) {
    if (rejectUnsafeKey(name, context))
      continue;
    const childSchema = propertySchema(schema, name);
    let effective = childSchema;
    if (!childSchema) {
      if (policy.kind === "drop") {
        context.path.push(name);
        record2(context, {
          code: "PILENS_CFG_0004",
          key: pointerOf(context.path),
          reason: "unknown config field; ignored"
        });
        context.path.pop();
        continue;
      }
      effective = policy.kind === "validate" ? policy.schema : void 0;
    }
    context.path.push(name);
    const walked = walk(child, effective, context, depth + 1, nextPath);
    context.path.pop();
    if (walked !== DROPPED)
      safeAssign(out, name, walked);
  }
  nextPath.delete(value);
  return out;
}
function rejectUnsafeKey(name, context) {
  if (!isUnsafeConfigKey(name))
    return false;
  context.path.push(name);
  record2(context, {
    code: "PILENS_CFG_0006",
    key: pointerOf(context.path),
    reason: UNSAFE_KEY_REASON
  });
  context.path.pop();
  return true;
}
function walkArray(value, schema, context, depth, onPath) {
  if (!Array.isArray(value)) {
    record2(context, {
      code: "PILENS_CFG_0005",
      key: pointerOf(context.path),
      reason: `expected an array, got ${jsonTypeName(value)}; ignored`
    });
    return DROPPED;
  }
  const nextPath = onPath.add(value);
  const items = itemsSchema(schema);
  const out = [];
  for (const [index, entry] of value.entries()) {
    context.path.push(String(index));
    const walked = walk(entry, items, context, depth + 1, nextPath);
    context.path.pop();
    if (walked !== DROPPED)
      out.push(walked);
  }
  nextPath.delete(value);
  return out;
}
var SCALAR_CHECKS = {
  string: (value) => typeof value === "string",
  number: (value) => typeof value === "number" && Number.isFinite(value),
  integer: (value) => typeof value === "number" && Number.isInteger(value),
  boolean: (value) => typeof value === "boolean",
  null: (value) => value === null
};
function walkScalar(value, schema, context, declared) {
  const check = SCALAR_CHECKS[declared];
  if (!check(value)) {
    record2(context, {
      code: "PILENS_CFG_0005",
      key: pointerOf(context.path),
      reason: `expected ${declared}, got ${jsonTypeName(value)}; ignored`
    });
    return DROPPED;
  }
  return checkEnum(value, schema, context) ? value : DROPPED;
}
function checkEnum(value, schema, context) {
  const allowed = schema?.enum;
  if (!Array.isArray(allowed))
    return true;
  if (allowed.some((candidate) => Object.is(candidate, value)))
    return true;
  record2(context, {
    code: "PILENS_CFG_0005",
    key: pointerOf(context.path),
    reason: `value is not one of ${allowed.map((candidate) => JSON.stringify(candidate)).join(", ")}; ignored`
  });
  return false;
}

// dist/clients/config-core/resolve.js
function anchorSource(sources) {
  try {
    const last = sources?.[sources.length - 1];
    return last === void 0 || typeof last.file !== "string" ? void 0 : { file: last.file, tier: last.tier };
  } catch {
    return void 0;
  }
}
function resolveConfig(options) {
  const collector = new MigrationRecordCollector(options.maxRecords);
  try {
    const normalized = options.sources.map((source) => ({
      tier: source.tier,
      ...source.file === void 0 ? {} : { file: source.file },
      ...source.trust === void 0 ? {} : { trust: source.trust },
      value: validate(source.value, options.schema, {
        file: source.file ?? "",
        tier: source.tier,
        collector
      }).value
    }));
    return {
      resolved: merge(normalized, options.schema, { collector }),
      records: collector.records,
      droppedRecordCount: collector.droppedCount
    };
  } catch (error) {
    const anchor = anchorSource(options.sources);
    const file = anchor?.file ?? "";
    collector.add({
      code: "PILENS_CFG_0008",
      file,
      key: "",
      subject: migrationSubject(file, ""),
      reason: `config resolution failed internally (${errorClassName(error)}); configuration ignored`,
      ...anchor ? { tier: anchor.tier } : {}
    });
    return {
      // The empty resolution, built by the merger from no sources rather than
      // asserted into existence: `merge([])` is already "nothing resolved".
      resolved: merge([], options.schema),
      records: collector.records,
      droppedRecordCount: collector.droppedCount
    };
  }
}

// dist/clients/config-schema.js
var CONFIG_SCHEMA_DIALECT = "https://json-schema.org/draft/2020-12/schema";
function opaque(tier) {
  return { [STABILITY_TIER_KEY]: tier };
}
var LSP_KEY_TYPES = {
  servers: "object",
  serverOverrides: "object",
  disabledServers: "array",
  warmFiles: "array"
};
var LSP_KEY_DENY = {
  disabledServers: "array-union"
};
function denyAnnotation(key) {
  const policy = LSP_KEY_DENY[key];
  return policy === void 0 ? {} : { [DENY_KEY]: policy };
}
function lspServerEntryNode() {
  return {
    type: "object",
    additionalProperties: true,
    [STABILITY_TIER_KEY]: "experimental",
    properties: {
      covers: {
        type: "array",
        items: { type: "string", [STABILITY_TIER_KEY]: "experimental" },
        [MERGE_STRATEGY_KEY]: "replace",
        [STABILITY_TIER_KEY]: "experimental"
      }
    }
  };
}
function lspNamespace() {
  const properties = {
    // Reserved but deliberately UNTYPED. `lens-config.ts` already rejects a
    // non-boolean here with `lsp.enabled must be a boolean` — a message that
    // names the key and the expected type — and it is the one every flag in
    // the registry shares. Declaring `type: "boolean"` would make `validate()`
    // drop the value first and replace that message with the core's generic
    // `expected boolean, got string`, which is strictly less useful. The flag
    // sections move into the schema when their diagnostics do, not before.
    enabled: { [STABILITY_TIER_KEY]: "stable" }
  };
  for (const key of LEGACY_ROOT_LSP_KEYS) {
    const declared = LSP_KEY_TYPES[key];
    properties[key] = {
      ...declared ? { type: declared } : {},
      [STABILITY_TIER_KEY]: "experimental",
      ...declared === "object" ? key === "servers" ? (
        // #3968: server entries are TYPED — each carries the covers
        // claim node — while staying open on every other field.
        { additionalProperties: lspServerEntryNode() }
      ) : { additionalProperties: true } : {},
      ...denyAnnotation(key)
    };
  }
  return {
    type: "object",
    additionalProperties: true,
    [STABILITY_TIER_KEY]: "stable",
    properties
  };
}
function buildConfigSchema() {
  const properties = {};
  for (const key of [
    ...flagConfigSectionKeys(LENS_FLAGS),
    ...GLOBAL_NON_FLAG_CONFIG_SECTIONS,
    ...PROJECT_NON_FLAG_CONFIG_SECTIONS
  ]) {
    properties[key] = opaque("stable");
  }
  properties.tools = {
    type: "object",
    additionalProperties: true,
    properties: Object.fromEntries(LENS_TOOL_NAMES.map((name) => [
      name,
      {
        type: "object",
        additionalProperties: true,
        properties: {
          enabled: { type: "boolean", [STABILITY_TIER_KEY]: "experimental" }
        },
        [STABILITY_TIER_KEY]: "experimental"
      }
    ])),
    [STABILITY_TIER_KEY]: "stable"
  };
  for (const key of PROJECT_FOREIGN_CONFIG_NAMESPACES) {
    properties[key] ??= opaque("experimental");
  }
  for (const key of [
    "knip",
    "jscpd",
    "madge",
    "gitleaks",
    "govulncheck",
    "deadCode",
    "complexity"
  ]) {
    properties[key] = {
      type: "object",
      additionalProperties: true,
      properties: {
        enabled: { type: "boolean", [STABILITY_TIER_KEY]: "experimental" }
      },
      [STABILITY_TIER_KEY]: "stable"
    };
  }
  properties.startup = {
    type: "object",
    additionalProperties: true,
    properties: {
      mode: {
        type: "string",
        enum: ["quick", "full", "minimal"],
        [STABILITY_TIER_KEY]: "experimental"
      },
      scans: {
        type: "object",
        additionalProperties: true,
        properties: {
          enabled: { type: "boolean", [STABILITY_TIER_KEY]: "experimental" }
        },
        [STABILITY_TIER_KEY]: "experimental"
      }
    },
    [STABILITY_TIER_KEY]: "stable"
  };
  for (const key of LEGACY_ROOT_LSP_KEYS) {
    properties[key] = opaque("experimental");
  }
  properties[LSP_NAMESPACE_KEY] = lspNamespace();
  properties[CONFIG_SCHEMA_ANCHOR_KEY] = {
    type: "string",
    description: "Identity anchor naming the schema version this config was written against.",
    [STABILITY_TIER_KEY]: "stable"
  };
  return {
    $schema: CONFIG_SCHEMA_DIALECT,
    $id: CONFIG_SCHEMA_ID,
    type: "object",
    // See shape 1 in the module doc: deliberately open, so the loaders keep
    // their own richer unknown-key diagnostics.
    additionalProperties: true,
    properties
  };
}
var PI_LENS_CONFIG_SCHEMA = buildConfigSchema();

// dist/clients/config-resolve.js
function readConfigDocument(file) {
  let text;
  try {
    text = fs2.readFileSync(file, "utf-8");
  } catch (error) {
    if (error instanceof Error && "code" in error && error.code === "ENOENT") {
      return { status: "missing" };
    }
    return { status: "error", error };
  }
  try {
    return { status: "ok", value: JSON.parse(text) };
  } catch (error) {
    return { status: "error", error, sourceText: text };
  }
}
function canonicalGlobalFile(options) {
  if (options.globalConfigPath !== void 0)
    return options.globalConfigPath;
  return options.globalDir === void 0 ? void 0 : path2.join(options.globalDir, CANONICAL_GLOBAL_CONFIG_FILE);
}
function collectDocuments(dir, locations, tier, onReadError) {
  const documents = [];
  for (const location of locations) {
    const file = path2.join(dir, location.relativePath);
    if (tier !== "global" && isResolvedGlobalConfigPath(file))
      continue;
    const outcome = readConfigDocument(file);
    if (outcome.status === "missing")
      continue;
    if (outcome.status === "error") {
      onReadError?.({
        file,
        location,
        tier,
        error: outcome.error,
        sourceText: outcome.sourceText
      });
      continue;
    }
    documents.push({ tier, file, location, value: outcome.value });
  }
  return documents;
}
function collectPiLensConfigDocuments(options) {
  const documents = [];
  const { globalDir, onReadError } = options;
  const globalConfigPath = canonicalGlobalFile(options);
  if (globalDir !== void 0 || globalConfigPath !== void 0) {
    for (const location of GLOBAL_CONFIG_LOCATIONS) {
      const file = location.relativePath === CANONICAL_GLOBAL_CONFIG_FILE ? globalConfigPath : globalDir === void 0 ? void 0 : path2.join(globalDir, location.relativePath);
      if (file === void 0)
        continue;
      const outcome = readConfigDocument(file);
      if (outcome.status === "missing")
        continue;
      if (outcome.status === "error") {
        onReadError?.({
          file,
          location,
          tier: "global",
          error: outcome.error,
          sourceText: outcome.sourceText
        });
        continue;
      }
      documents.push({ tier: "global", file, location, value: outcome.value });
    }
  }
  if (options.cwd !== void 0) {
    const dirs = configSearchDirs(options.cwd, options.homeDir ?? os.homedir()).reverse();
    const bearing = dirs.map((dir) => ({
      dir,
      found: collectDocuments(dir, PROJECT_CONFIG_LOCATIONS, "project", onReadError)
    })).filter((entry) => entry.found.length > 0);
    bearing.forEach((entry, index) => {
      const tier = index === 0 ? "project" : "nested-project";
      for (const document of entry.found)
        documents.push({ ...document, tier });
    });
  }
  return documents;
}
function emptyTierCounts() {
  return Object.fromEntries(SOURCE_TIERS.map((tier) => [tier, 0]));
}
function summarizeConfigResolution(resolution, homeDir) {
  const countsByTier = emptyTierCounts();
  for (const entry of resolution.provenance.values()) {
    countsByTier[entry.tier] += 1;
  }
  return {
    documents: resolution.documents.map((document) => ({
      tier: document.tier,
      file: homeRelativePath(document.file, homeDir),
      legacy: document.location.legacy
    })),
    countsByTier,
    recordCount: resolution.records.length
  };
}
function resolvePiLensConfig(options) {
  const documents = collectPiLensConfigDocuments(options);
  const sources = configSources(documents);
  const resolution = resolveConfig({
    sources,
    schema: PI_LENS_CONFIG_SCHEMA
  });
  return {
    value: resolution.resolved.value ?? {},
    provenance: resolution.resolved.provenance,
    records: finalizeRecords(
      [
        ...resolution.records,
        ...deprecationRecords(documents, options.homeDir ?? os.homedir(), canonicalGlobalFile(options))
      ],
      documentAnchor(documents),
      // The core ALREADY truncated (#2426 review round 6, F1). Its drop count
      // is seeded here so the one `PILENS_CFG_0007` a user reads names the
      // whole truncation rather than the part this bound performed.
      resolution.droppedRecordCount
    ),
    documents
  };
}
function resolveOnePiLensConfigDocument(document, homeDir = os.homedir()) {
  const resolution = resolveConfig({
    sources: [
      { tier: document.tier, file: document.file, value: document.value }
    ],
    schema: PI_LENS_CONFIG_SCHEMA
  });
  return {
    value: resolution.resolved.value ?? {},
    records: finalizeRecords(
      [...resolution.records, ...deprecationRecords([document], homeDir)],
      documentAnchor([document]),
      // Same double bound as the multi-document path above (round 6, F1).
      resolution.droppedRecordCount
    )
  };
}
function documentAnchor(documents) {
  const last = documents[documents.length - 1];
  return last === void 0 ? void 0 : { file: last.file, tier: last.tier };
}
function projectLocationFor(file) {
  const base = path2.basename(file);
  return PROJECT_CONFIG_LOCATIONS.find((candidate) => path2.posix.basename(candidate.relativePath) === base) ?? PROJECT_CONFIG_LOCATIONS[PROJECT_CONFIG_LOCATIONS.length - 1];
}
var WINDOW_BY_SURFACE = new Map(DEPRECATED_CONFIG_SURFACES.map((row) => [row.surface, row]));
function windowSuffix(surface) {
  const row = surface === void 0 ? void 0 : WINDOW_BY_SURFACE.get(surface);
  return row ? ` (deprecated since ${row.deprecatedSince}; read for the last time before ${row.removeNotBefore})` : "";
}
function canonicalDestination(document, homeDir, globalFile) {
  if (document.tier === "global") {
    return homeRelativePath(globalFile ?? path2.join(path2.dirname(document.file), CANONICAL_GLOBAL_CONFIG_FILE), homeDir);
  }
  return CANONICAL_PROJECT_CONFIG_FILE;
}
function lspNamespaceKeyFor(document, key) {
  if (key === LSP_NAMESPACE_KEY)
    return void 0;
  if (document.location.lspScoped) {
    return recognizedKeysFor(document.location).has(key) ? key : void 0;
  }
  return LEGACY_ROOT_LSP_KEYS.includes(key) ? key : void 0;
}
function assignRaw(target, key, value) {
  Object.defineProperty(target, key, {
    value,
    writable: true,
    enumerable: true,
    configurable: true
  });
}
function configSources(documents) {
  const sources = [];
  for (const document of documents) {
    const value = document.value;
    const namespaced = {};
    const rest = {};
    for (const key of topLevelKeys(value)) {
      const entry = value[key];
      const target = lspNamespaceKeyFor(document, key);
      if (target === void 0)
        assignRaw(rest, key, entry);
      else
        assignRaw(namespaced, target, entry);
    }
    const base = { tier: document.tier, file: document.file };
    if (Object.keys(namespaced).length === 0) {
      sources.push({ ...base, value });
      continue;
    }
    sources.push({ ...base, value: { [LSP_NAMESPACE_KEY]: namespaced } });
    sources.push({ ...base, value: rest });
  }
  return sources;
}
function canonicalKeyFor(document, key) {
  const namespaced = lspNamespaceKeyFor(document, key);
  return namespaced === void 0 ? key : `${LSP_NAMESPACE_KEY}.${namespaced}`;
}
function topLevelKeys(value) {
  return value && typeof value === "object" && !Array.isArray(value) ? Object.keys(value) : [];
}
function schemaPropertyKeys(node) {
  const properties = node?.properties;
  return properties !== null && typeof properties === "object" && !Array.isArray(properties) ? Object.keys(properties) : [];
}
var CANONICAL_TOP_LEVEL_KEYS = new Set(schemaPropertyKeys(PI_LENS_CONFIG_SCHEMA));
var LSP_DOCUMENT_KEYS = new Set(schemaPropertyKeys(PI_LENS_CONFIG_SCHEMA.properties?.[LSP_NAMESPACE_KEY]));
function recognizedKeysFor(location) {
  return location.lspScoped ? LSP_DOCUMENT_KEYS : CANONICAL_TOP_LEVEL_KEYS;
}
function deprecationRecords(documents, homeDir, globalFile) {
  const records = [];
  for (const document of documents) {
    if (document.location.legacy) {
      const destination = canonicalDestination(document, homeDir, globalFile);
      const recognized = recognizedKeysFor(document.location);
      let unrecognized = 0;
      for (const key of topLevelKeys(document.value)) {
        if (!recognized.has(key)) {
          unrecognized += 1;
          continue;
        }
        const canonicalKey = canonicalKeyFor(document, key);
        records.push(record3({
          code: "PILENS_CFG_0003",
          file: document.file,
          key,
          canonicalKey,
          tier: document.tier,
          reason: `move "${key}" to ${destination}` + (canonicalKey === key ? "" : ` under "${canonicalKey}"`) + windowSuffix(document.location.surface)
        }));
      }
      if (unrecognized > 0) {
        const plural = unrecognized === 1 ? "it was" : "they were";
        records.push({
          code: "PILENS_CFG_0003",
          file: document.file,
          key: "",
          // An LSP-scoped legacy file moves WHOLESALE into the `lsp`
          // namespace, so naming that namespace is literally true here — and
          // it is what routes this record to the LSP loader's subsystem, the
          // same key-derived ownership every other record uses.
          ...document.location.lspScoped ? { canonicalKey: LSP_NAMESPACE_KEY } : {},
          subject: migrationSubject(document.file, ""),
          tier: document.tier,
          reason: `this file is at a deprecated location; move it to ${destination}. ` + (unrecognized === 1 ? "1 of its top-level keys is not a recognized pi-lens setting" : `${unrecognized} of its top-level keys are not recognized pi-lens settings`) + `, and ${plural} not migrated${windowSuffix(document.location.surface)}`
        });
      }
      continue;
    }
    for (const key of topLevelKeys(document.value)) {
      if (!LEGACY_ROOT_LSP_KEYS.includes(key))
        continue;
      const canonicalKey = `${LSP_NAMESPACE_KEY}.${key}`;
      records.push(record3({
        code: "PILENS_CFG_0002",
        file: document.file,
        key,
        canonicalKey,
        tier: document.tier,
        reason: `move "${key}" to "${canonicalKey}" in the same file${windowSuffix(key)}`
      }));
    }
  }
  return records;
}
function record3(input) {
  return {
    code: input.code,
    file: input.file,
    key: input.key,
    canonicalKey: input.canonicalKey,
    subject: migrationSubject(input.file, input.key),
    reason: input.reason,
    tier: input.tier
  };
}
function configRecordOwner(record4) {
  const key = record4.canonicalKey ?? record4.key;
  if (key.length === 0)
    return void 0;
  const head = (key.startsWith("/") ? key.slice(1) : key).split(/[./]/)[0] ?? "";
  if (head.length === 0)
    return void 0;
  return head === LSP_NAMESPACE_KEY || LEGACY_ROOT_LSP_KEYS.includes(head) ? "lsp" : "pi-lens";
}
function piLensSubsystemFor(tier) {
  if (tier === "global")
    return "lens-config";
  if (tier === "project" || tier === "nested-project") {
    return "project-lens-config";
  }
  return void 0;
}
function configRecordSubsystems(record4) {
  const owner = configRecordOwner(record4);
  const piLens = piLensSubsystemFor(record4.tier);
  const piLensSubsystems = piLens ? [piLens] : ["lens-config", "project-lens-config"];
  if (owner === "lsp")
    return ["lsp-config"];
  if (owner === "pi-lens")
    return piLensSubsystems;
  if (piLens)
    return [piLens];
  return ["lsp-config", ...piLensSubsystems];
}
function configFileSubsystem(location, tier) {
  if (location.lspScoped)
    return "lsp-config";
  return tier === "global" ? "lens-config" : "project-lens-config";
}
function reportConfigReadFailure(failure) {
  warnIgnoredConfigOnce({
    subsystem: configFileSubsystem(failure.location, failure.tier),
    file: failure.file,
    reason: { parseError: failure.error, sourceText: failure.sourceText }
  });
}
function reportPiLensConfigRecords(records) {
  for (const record4 of records) {
    for (const subsystem of configRecordSubsystems(record4)) {
      warnIgnoredConfigOnce({
        subsystem,
        file: record4.file,
        key: record4.key.length > 0 ? record4.key : void 0,
        reason: record4.reason,
        code: record4.code
      });
    }
  }
}
function ignoredRecordCollector(configPath, tier) {
  const noted = [];
  const note = (reason, code = "PILENS_CFG_0001") => {
    let notedRecord = {
      code: "PILENS_CFG_0001",
      file: configPath,
      key: "",
      subject: migrationSubject(configPath, ""),
      reason: typeof reason === "string" ? reason : normalizeParseErrorReason(reason.parseError, {
        sourceText: reason.sourceText
      }),
      tier
    };
    if (code !== "PILENS_CFG_0001")
      notedRecord.code = code;
    noted.push(notedRecord);
  };
  return {
    note,
    records: () => finalizeRecords(noted, { file: configPath, tier })
  };
}
function lspSectionOf(value) {
  const namespace = value[LSP_NAMESPACE_KEY];
  return namespace && typeof namespace === "object" && !Array.isArray(namespace) ? namespace : {};
}

// dist/clients/project-lens-config.js
var REVIEW_GRAPH_MAX_FILES_MIN = 100;
var REVIEW_GRAPH_MAX_FILES_MAX = 2e4;
var EMPTY_PROJECT_CONFIG = {
  ignore: [],
  rules: {},
  maxProjectFiles: void 0,
  reviewGraph: void 0,
  raw: void 0,
  configPath: void 0
};
var configCache = /* @__PURE__ */ new Map();
var discoveryCache = /* @__PURE__ */ new Map();
function loadCachedConfigFile(info) {
  const cached = configCache.get(info.path);
  if (cached && cached.mtimeMs === info.mtimeMs && cached.size === info.size) {
    replayConfigNotices(cached);
    return cached.config;
  }
  const parsed = parseConfigFile(info.path);
  const entry = {
    mtimeMs: info.mtimeMs,
    size: info.size,
    config: parsed.config,
    records: parsed.records,
    ignored: parsed.ignored
  };
  configCache.set(info.path, entry);
  replayConfigNotices(entry);
  return parsed.config;
}
function replayConfigNotices(entry) {
  reportPiLensConfigRecords(entry.records);
  for (const record4 of entry.ignored) {
    warnIgnoredConfigOnce({
      subsystem: "project-lens-config",
      file: record4.file,
      reason: record4.reason,
      code: record4.code
    });
  }
}
function loadPiLensProjectConfig(startDir, preloadedInfo) {
  const entry = discoveryEntryFor(startDir);
  const configInfo = preloadedInfo ?? freshInfoFor(entry);
  const selectedLegacyPath = configInfo?.path;
  reportPiLensConfigRecords(entry.legacyRecords.filter((record4) => record4.file !== selectedLegacyPath));
  if (!configInfo)
    return EMPTY_PROJECT_CONFIG;
  return loadCachedConfigFile(configInfo);
}
function findPiLensConfigInDir(dir) {
  const marker = findPiLensConfigMarkerInDir(dir);
  if (!marker)
    return void 0;
  return {
    path: marker.path,
    dir: marker.dir,
    mtimeMs: marker.mtimeMs,
    size: marker.size
  };
}
function findNestedProjectMutationValue(spec, editedFilePath, projectRoot, homeDir = os2.homedir()) {
  const root = path3.resolve(projectRoot);
  const start = path3.dirname(path3.resolve(editedFilePath));
  for (const dir of walkUpDirs(start)) {
    if (isAtOrAboveHomeDir(dir, homeDir))
      break;
    const rel = path3.relative(root, dir);
    if (rel.startsWith("..") || path3.isAbsolute(rel))
      break;
    const config = loadPiLensConfigInDir(dir);
    const enabled = readFlagConfigValue(config, spec.configKey);
    if (enabled !== void 0)
      return { value: enabled, dir };
    if (dir === root)
      break;
  }
  return void 0;
}
function loadPiLensConfigInDir(dir) {
  const info = findPiLensConfigInDir(dir);
  if (!info)
    return EMPTY_PROJECT_CONFIG;
  return loadCachedConfigFile(info);
}
function discoveryEntryFor(startDir) {
  const cacheKey = path3.resolve(startDir);
  const cached = discoveryCache.get(cacheKey);
  if (cached && discoveryCacheStillFresh(cached))
    return cached;
  const discovered = discoverPiLensProjectConfig(cacheKey);
  discoveryCache.set(cacheKey, discovered);
  return discovered;
}
function findPiLensProjectConfig(startDir) {
  return freshInfoFor(discoveryEntryFor(startDir));
}
function freshInfoFor(entry) {
  if (!entry.info)
    return void 0;
  const stat = safeFileStat(entry.info.path);
  if (!stat?.isFile())
    return entry.info;
  return { ...entry.info, mtimeMs: stat.mtimeMs, size: stat.size };
}
function safeFileStat(filePath) {
  try {
    return fs3.statSync(filePath);
  } catch {
    return void 0;
  }
}
function safeDirMtimeMs(dir) {
  try {
    return fs3.statSync(dir).mtimeMs;
  } catch {
    return -1;
  }
}
function discoveryCacheStillFresh(entry) {
  if (!dirMtimesStillFresh(entry.dirMtimes)) {
    return false;
  }
  if (entry.info === void 0 && Date.now() - entry.checkedAtMs >= FRESHNESS_CADENCE_MS) {
    return false;
  }
  return entry.legacySources.every((source) => {
    const stat = safeFileStat(source.path);
    return stat?.isFile() === true && stat.mtimeMs === source.mtimeMs && stat.size === source.size;
  });
}
var LEGACY_PROJECT_LOCATIONS = PROJECT_CONFIG_LOCATIONS.filter((location) => location.legacy);
function discoverPiLensProjectConfig(startDir) {
  const dirMtimes = [];
  const legacyDocuments = [];
  const legacySources = [];
  let info;
  for (const dir of configSearchDirs(startDir)) {
    dirMtimes.push({ dir, mtimeMs: safeDirMtimeMs(dir) });
    if (!info) {
      for (const name of PROJECT_CONFIG_BASENAMES) {
        const candidate = path3.join(dir, name);
        if (isResolvedGlobalConfigPath(candidate))
          continue;
        const stat = safeFileStat(candidate);
        if (stat?.isFile()) {
          info = {
            path: candidate,
            dir,
            mtimeMs: stat.mtimeMs,
            size: stat.size
          };
          break;
        }
      }
    }
    for (const location of LEGACY_PROJECT_LOCATIONS) {
      const file = path3.join(dir, location.relativePath);
      if (isResolvedGlobalConfigPath(file))
        continue;
      const stat = safeFileStat(file);
      if (!stat?.isFile())
        continue;
      legacySources.push({
        path: file,
        mtimeMs: stat.mtimeMs,
        size: stat.size
      });
      const outcome = readConfigDocument(file);
      if (outcome.status === "error") {
        reportConfigReadFailure({
          file,
          location,
          tier: "project",
          error: outcome.error,
          sourceText: outcome.sourceText
        });
        continue;
      }
      if (outcome.status !== "ok")
        continue;
      legacyDocuments.push({
        tier: "project",
        file,
        location,
        value: outcome.value
      });
    }
  }
  return {
    info,
    dirMtimes: bearingDirMtimes(dirMtimes, info),
    legacyRecords: legacyDeprecationRecords(legacyDocuments),
    legacySources,
    checkedAtMs: Date.now()
  };
}
function legacyDeprecationRecords(documents) {
  const home = os2.homedir();
  return documents.flatMap((document) => finalizeRecords(deprecationRecords([document], home), {
    file: document.file,
    tier: document.tier
  }));
}
function bearingDirMtimes(dirMtimes, info) {
  if (info) {
    const bearing = dirMtimes.findIndex((entry) => entry.dir === info.dir);
    return bearing < 0 ? [...dirMtimes] : dirMtimes.slice(0, bearing + 1);
  }
  return dirMtimes.slice(0, 1);
}
function parseRulePolicyList(note, ruleId, key, value) {
  if (!Array.isArray(value)) {
    note(`rules.${ruleId}.${key} must be an array of strings`);
    return { list: [], invalid: true };
  }
  const list = [];
  for (const entry of value) {
    if (typeof entry !== "string")
      continue;
    const trimmed = entry.trim();
    if (trimmed.length > 0)
      list.push(trimmed);
  }
  if (list.length === 0) {
    if (value.length > 0) {
      note(`rules.${ruleId}.${key} must contain at least one non-empty string`);
      return { list: [], invalid: true };
    }
    return { list: [], invalid: false };
  }
  return { list, invalid: false };
}
var NO_RECORDS = [];
function parseConfigFile(configPath) {
  const { note, records: ignoredRecords } = ignoredRecordCollector(configPath, "project");
  const outcome = readConfigDocument(configPath);
  if (outcome.status === "error") {
    note({ parseError: outcome.error, sourceText: outcome.sourceText });
    return {
      config: EMPTY_PROJECT_CONFIG,
      records: NO_RECORDS,
      ignored: ignoredRecords()
    };
  }
  if (outcome.status === "missing") {
    return {
      config: EMPTY_PROJECT_CONFIG,
      records: NO_RECORDS,
      ignored: NO_RECORDS
    };
  }
  if (!outcome.value || typeof outcome.value !== "object" || Array.isArray(outcome.value)) {
    note("top-level value must be an object");
    return {
      config: EMPTY_PROJECT_CONFIG,
      records: NO_RECORDS,
      ignored: ignoredRecords()
    };
  }
  const resolved = resolveOnePiLensConfigDocument({
    tier: "project",
    file: configPath,
    location: projectLocationFor(configPath),
    value: outcome.value
  });
  const raw = resolved.value;
  const obj = resolved.value;
  const ignore = Array.isArray(obj.ignore) ? obj.ignore.filter((p) => typeof p === "string") : [];
  const mutations = {};
  for (const spec of PROJECT_SCOPED_LENS_FLAGS) {
    assignFlagConfigSection(obj, mutations, spec.configKey, note);
  }
  const toolConfig = readToolConfig(obj, note);
  const rules = {};
  if (obj.rules && typeof obj.rules === "object" && !Array.isArray(obj.rules)) {
    const rawRules = obj.rules;
    for (const [ruleId, ruleCfg] of Object.entries(rawRules)) {
      if (!ruleCfg || typeof ruleCfg !== "object" || Array.isArray(ruleCfg)) {
        note(`rules.${ruleId} must be an object with threshold, disable, or select; ignored`);
        continue;
      }
      const r = ruleCfg;
      const entry = {};
      if (typeof r.threshold === "number" && Number.isFinite(r.threshold) && r.threshold > 0) {
        entry.threshold = r.threshold;
      } else if ("threshold" in r) {
        note(`rules.${ruleId}.threshold must be a positive finite number`);
      }
      if ("disable" in r) {
        const parsed = parseRulePolicyList(note, ruleId, "disable", r.disable);
        if (!parsed.invalid && parsed.list.length > 0)
          entry.disable = parsed.list;
      }
      if ("select" in r) {
        const parsed = parseRulePolicyList(note, ruleId, "select", r.select);
        if (!parsed.invalid && parsed.list.length > 0)
          entry.select = parsed.list;
      }
      if (entry.threshold !== void 0 || entry.disable || entry.select) {
        rules[ruleId] = entry;
      } else if (!("threshold" in r) && !("disable" in r) && !("select" in r)) {
        note(`rules.${ruleId} has no recognized setting (threshold, disable, select); ignored`);
      }
    }
  }
  let maxProjectFiles;
  if ("maxProjectFiles" in obj) {
    if (typeof obj.maxProjectFiles === "number" && Number.isFinite(obj.maxProjectFiles) && obj.maxProjectFiles > 0) {
      maxProjectFiles = obj.maxProjectFiles;
    } else {
      note("maxProjectFiles must be a positive finite number");
    }
  }
  let reviewGraph;
  if (obj.reviewGraph !== void 0) {
    if (!obj.reviewGraph || typeof obj.reviewGraph !== "object" || Array.isArray(obj.reviewGraph)) {
      note("reviewGraph must be an object");
    } else {
      const rg = obj.reviewGraph;
      if ("maxFiles" in rg) {
        const parsed = toPositiveFinite(rg.maxFiles);
        if (parsed > 0) {
          const clamped = Math.min(REVIEW_GRAPH_MAX_FILES_MAX, Math.max(REVIEW_GRAPH_MAX_FILES_MIN, Math.floor(parsed)));
          reviewGraph = { maxFiles: clamped };
        } else {
          note("reviewGraph.maxFiles must be a positive finite number");
        }
      }
    }
  }
  let startup;
  if (obj.startup !== void 0) {
    if (!obj.startup || typeof obj.startup !== "object" || Array.isArray(obj.startup)) {
      note("startup must be an object");
    } else {
      const section = obj.startup;
      if (section.mode === "quick" || section.mode === "full" || section.mode === "minimal") {
        startup = { mode: section.mode };
      } else if ("mode" in section) {
        note('startup.mode must be "quick", "full", or "minimal"');
      }
      if (section.scans !== void 0) {
        if (!section.scans || typeof section.scans !== "object" || Array.isArray(section.scans)) {
          note("startup.scans must be an object");
        } else if (typeof section.scans.enabled === "boolean") {
          startup ??= {};
          startup.scans = {
            enabled: section.scans.enabled
          };
        } else if ("enabled" in section.scans) {
          note("startup.scans.enabled must be a boolean");
        }
      }
    }
  }
  const knownProjectKeys = /* @__PURE__ */ new Set([
    ...PROJECT_NON_FLAG_CONFIG_SECTIONS,
    ...flagConfigSectionKeys(PROJECT_SCOPED_LENS_FLAGS),
    ...PROJECT_FOREIGN_CONFIG_NAMESPACES
  ]);
  const globalScopeOnlyKeys = new Set([
    ...flagConfigSectionKeys(LENS_FLAGS),
    ...GLOBAL_NON_FLAG_CONFIG_SECTIONS
  ].filter((key) => !knownProjectKeys.has(key)));
  const projectScoped = new Set(PROJECT_SCOPED_LENS_FLAGS.map((spec) => spec.configKey));
  const globalScopeOnlyFlagKeys = LENS_FLAGS.map((spec) => spec.configKey).filter((configKey) => !projectScoped.has(configKey));
  const globalScopeOnlyDottedKeys = [
    ...globalScopeOnlyFlagKeys,
    ...GLOBAL_ONLY_NON_FLAG_KEYS
  ];
  const globalConfigLabel = homeRelativePath(getPiLensGlobalConfigPath(), os2.homedir());
  const warnUnhonoredKey = (label, globalOnly) => {
    if (!globalOnly) {
      note(`unknown key "${label}" is not a recognized pi-lens setting (check for a typo); ignored`);
      return;
    }
    const hasCliFlag = LENS_FLAGS.some(({ configKey }) => configKey === label || configKey.startsWith(`${label}.`));
    const cliClause = hasCliFlag ? " or pass the matching CLI flag" : "";
    note(`"${label}" is a global-only pi-lens setting and is not honored in a project .pi-lens.json (set it in ${globalConfigLabel}${cliClause}); ignored`);
  };
  for (const key of Object.keys(obj)) {
    if (knownProjectKeys.has(key))
      continue;
    warnUnhonoredKey(key, globalScopeOnlyKeys.has(key));
  }
  for (const [namespace, honored] of PROJECT_FOREIGN_NAMESPACE_HONORED_KEYS) {
    const section = obj[namespace];
    if (!section || typeof section !== "object" || Array.isArray(section)) {
      continue;
    }
    for (const key of Object.keys(section)) {
      if (honored.includes(key))
        continue;
      const dotted = `${namespace}.${key}`;
      warnUnhonoredKey(dotted, globalScopeOnlyDottedKeys.some((configKey) => configKey === dotted || configKey.startsWith(`${dotted}.`)));
    }
  }
  for (const configKey of globalScopeOnlyDottedKeys) {
    const dotIndex = configKey.indexOf(".");
    if (dotIndex < 0)
      continue;
    if (!knownProjectKeys.has(configKey.slice(0, dotIndex)))
      continue;
    if (!hasFlagConfigPath(obj, configKey))
      continue;
    warnUnhonoredKey(configKey, true);
  }
  return {
    config: {
      ...toolConfig === void 0 ? {} : { tools: toolConfig },
      ...startup === void 0 ? {} : { startup },
      ignore,
      rules,
      ...mutations,
      maxProjectFiles,
      reviewGraph,
      raw,
      configPath
    },
    records: resolved.records,
    ignored: ignoredRecords()
  };
}

export {
  isGeneratedArtifactDirectoryName,
  isDeclarationFile,
  classifyGeneratedOrArtifactDetailed,
  isTestFileName,
  detectFileRole,
  notifyUserDegradation,
  warnIgnoredConfigOnce,
  provenanceView,
  provenanceFor,
  compareKeys,
  migrationSubject,
  readConfigDocument,
  summarizeConfigResolution,
  resolvePiLensConfig,
  resolveOnePiLensConfigDocument,
  reportConfigReadFailure,
  reportPiLensConfigRecords,
  ignoredRecordCollector,
  lspSectionOf,
  loadPiLensProjectConfig,
  findPiLensConfigInDir,
  findNestedProjectMutationValue,
  loadPiLensConfigInDir,
  findPiLensProjectConfig
};