'use strict';

const fs = require('fs');
const path = require('path');

const MCP_FAILURE_SETTING = 'codex_mcp_launch_failures_v1';
const MCP_FAILURE_TTL_MS = 10 * 60 * 1000;
const SKILL_DESCRIPTION_MAX = 1024;
const SKILL_DESCRIPTION_REPAIR_TARGET = 640;
const PROTECTED_MCP_SERVERS = new Set(['wall-e']);
const PROJECT_TRUST_ENV = 'CTM_CODEX_PROJECT_TRUST';
const HTTP_TRANSPORT_ENV = 'CTM_CODEX_HTTP_TRANSPORT';
const HTTP_TRANSPORT_PROVIDER_ID = 'ctm-openai-https';

function isCodexCommand(cmd) {
  return path.basename(String(cmd || '')).toLowerCase() === 'codex';
}

function codexHome(options = {}) {
  return options.codexHome
    || options.env?.CODEX_HOME
    || process.env.CODEX_HOME
    || path.join(options.homeDir || process.env.HOME || process.env.USERPROFILE || '', '.codex');
}

function readText(filePath) {
  try { return fs.readFileSync(filePath, 'utf8'); } catch { return ''; }
}

function parseTomlDottedKeySegments(value) {
  const src = String(value || '');
  const segments = [];
  let i = 0;
  const skipWs = () => {
    while (i < src.length && /\s/.test(src[i])) i += 1;
  };
  while (i < src.length) {
    skipWs();
    if (i >= src.length) break;
    const quote = src[i] === '"' || src[i] === "'" ? src[i++] : '';
    if (quote) {
      let segment = '';
      let closed = false;
      while (i < src.length) {
        const ch = src[i++];
        if (quote === '"' && ch === '\\' && i < src.length) {
          segment += src[i++];
          continue;
        }
        if (ch === quote) {
          closed = true;
          break;
        }
        segment += ch;
      }
      if (!closed) return null;
      segments.push(segment);
    } else {
      const start = i;
      while (i < src.length && src[i] !== '.') i += 1;
      const segment = src.slice(start, i).trim();
      if (!segment) return null;
      segments.push(segment);
    }
    skipWs();
    if (i >= src.length) break;
    if (src[i] !== '.') return null;
    i += 1;
  }
  return segments.length ? segments : null;
}

function parseTomlTableHeader(line) {
  const trimmed = String(line || '').trim();
  if (!trimmed.startsWith('[')) return null;
  const isArray = trimmed.startsWith('[[');
  const openLen = isArray ? 2 : 1;
  const closeLen = isArray ? 2 : 1;
  let quote = '';
  let escaped = false;
  let end = -1;
  for (let i = openLen; i < trimmed.length; i += 1) {
    const ch = trimmed[i];
    if (quote) {
      if (quote === '"' && escaped) {
        escaped = false;
        continue;
      }
      if (quote === '"' && ch === '\\') {
        escaped = true;
        continue;
      }
      if (ch === quote) quote = '';
      continue;
    }
    if (ch === '"' || ch === "'") {
      quote = ch;
      continue;
    }
    if (isArray ? (ch === ']' && trimmed[i + 1] === ']') : ch === ']') {
      end = i;
      break;
    }
  }
  if (end < 0) return null;
  const rest = trimmed.slice(end + closeLen).trim();
  if (rest && !rest.startsWith('#')) return null;
  const segments = parseTomlDottedKeySegments(trimmed.slice(openLen, end));
  return segments ? { segments, isArray } : null;
}

function stripTomlComment(value) {
  const src = String(value || '');
  let quote = '';
  let escaped = false;
  for (let i = 0; i < src.length; i += 1) {
    const ch = src[i];
    if (quote) {
      if (quote === '"' && escaped) {
        escaped = false;
        continue;
      }
      if (quote === '"' && ch === '\\') {
        escaped = true;
        continue;
      }
      if (ch === quote) quote = '';
      continue;
    }
    if (ch === '"' || ch === "'") {
      quote = ch;
      continue;
    }
    if (ch === '#') return src.slice(0, i).trim();
  }
  return src.trim();
}

function parseTomlScalar(value) {
  const raw = stripTomlComment(value);
  if (/^(true|false)$/i.test(raw)) return raw.toLowerCase() === 'true';
  const quoted = raw.match(/^"((?:\\.|[^"])*)"$/) || raw.match(/^'([^']*)'$/);
  if (quoted) {
    try { return JSON.parse(raw); } catch { return quoted[1]; }
  }
  if (raw.startsWith('[') && raw.endsWith(']')) {
    const body = raw.slice(1, -1);
    const items = [];
    let current = '';
    let quote = '';
    let escaped = false;
    for (let i = 0; i < body.length; i += 1) {
      const ch = body[i];
      if (quote) {
        current += ch;
        if (quote === '"' && escaped) {
          escaped = false;
          continue;
        }
        if (quote === '"' && ch === '\\') {
          escaped = true;
          continue;
        }
        if (ch === quote) quote = '';
        continue;
      }
      if (ch === '"' || ch === "'") {
        quote = ch;
        current += ch;
        continue;
      }
      if (ch === ',') {
        if (current.trim()) items.push(parseTomlScalar(current.trim()));
        current = '';
      } else {
        current += ch;
      }
    }
    if (current.trim()) items.push(parseTomlScalar(current.trim()));
    return items;
  }
  return raw;
}

function parseCodexMcpServers(configText) {
  const servers = new Map();
  let current = null;
  let nested = '';
  for (const rawLine of String(configText || '').split(/\r?\n/)) {
    const header = parseTomlTableHeader(rawLine);
    if (header) {
      if (header.segments[0] === 'mcp_servers' && header.segments[1]) {
        current = header.segments[1];
        nested = header.segments.slice(2).join('.');
        if (!servers.has(current)) servers.set(current, { name: current });
      } else {
        current = null;
        nested = '';
      }
      continue;
    }
    if (!current) continue;
    const trimmed = rawLine.trim();
    if (!trimmed || trimmed.startsWith('#')) continue;
    const match = trimmed.match(/^([A-Za-z0-9_.-]+)\s*=\s*(.*)$/);
    if (!match) continue;
    const server = servers.get(current);
    const key = nested ? `${nested}.${match[1]}` : match[1];
    const value = parseTomlScalar(match[2]);
    if (key === 'enabled') server.enabled = value !== false;
    if (key === 'url') server.url = String(value || '');
    if (key === 'command') server.command = String(value || '');
    if (key === 'type') server.type = String(value || '');
    if (key === 'args') server.args = Array.isArray(value) ? value : [];
    if (key === 'bearer_token_env_var') server.bearerTokenEnvVar = String(value || '');
    if (nested === 'http_headers') server.hasHttpHeaders = true;
    if (nested === 'env') server.hasEnv = true;
  }
  return Array.from(servers.values()).map((server) => ({
    ...server,
    enabled: server.enabled !== false,
    transport: server.url ? 'http' : 'stdio',
  }));
}

function codexMcpConfigPath(options = {}) {
  if (typeof options === 'string') {
    return path.join(codexHome({ homeDir: options }), 'config.toml');
  }
  return path.join(codexHome(options), 'config.toml');
}

function readCodexMcpServers(options = {}) {
  return parseCodexMcpServers(readText(codexMcpConfigPath(options)));
}

function stripAnsi(value) {
  return String(value || '')
    .replace(/\x1b\[[0-9;?]*[ -/]*[@-~]/g, '')
    .replace(/\x1b\][^\x07]*(?:\x07|\x1b\\)/g, '')
    .replace(/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g, '');
}

function extractCodexMcpFailures(text) {
  const clean = stripAnsi(text).replace(/\u00a0/g, ' ');
  const out = [];
  const seen = new Set();
  const push = (server, reason, sample) => {
    const name = String(server || '').trim();
    if (!name || seen.has(`${name}:${reason}`)) return;
    seen.add(`${name}:${reason}`);
    out.push({ server: name, reason, sample: sanitizeFailureSample(sample) });
  };
  for (const match of clean.matchAll(/The\s+([\w.-]+)\s+MCP server is not logged in\.[^\n\r]*/gi)) {
    push(match[1], 'not_logged_in', match[0]);
  }
  for (const match of clean.matchAll(/MCP client for\s+[`'"]?([^`'":]+)[`'"]?\s+failed to start:([^\n\r]*(?:\n[^\n\r]*){0,2})/gi)) {
    const body = match[2] || '';
    let reason = 'startup_failed';
    if (/Environment variable\s+[A-Za-z_][A-Za-z0-9_]*\s+.*not set|missing\s+(?:required\s+)?(?:environment variable|env var)/i.test(body)) reason = 'missing_env';
    else if (/OAuth token refresh failed|Token request failed/i.test(body)) reason = 'oauth_token_refresh_failed';
    else if (/not logged in/i.test(body)) reason = 'not_logged_in';
    else if (/\b(?:401|403|unauthori[sz]ed|forbidden)\b/i.test(body)) reason = 'auth_failed';
    push(match[1], reason, match[0]);
  }
  return out;
}

function sanitizeFailureSample(value) {
  return String(value || '')
    .replace(/\s+/g, ' ')
    .replace(/\bsk-[A-Za-z0-9_-]{8,}\b/g, '[redacted-key]')
    .replace(/\bxox[baprs]-[A-Za-z0-9-]{8,}\b/g, '[redacted-token]')
    .replace(/\bghp_[A-Za-z0-9_]{8,}\b/g, '[redacted-token]')
    .replace(/\bglpat-[A-Za-z0-9_-]{8,}\b/g, '[redacted-token]')
    .replace(/\b(?:Bearer|token|access_token|refresh_token)\s*[:=]\s*[^,\s;]+/gi, '$1=[redacted]')
    .trim()
    .slice(0, 300);
}

function getFailureState(dbModule) {
  try {
    const value = dbModule?.getSetting?.(MCP_FAILURE_SETTING, {});
    return value && typeof value === 'object' && !Array.isArray(value) ? value : {};
  } catch {
    return {};
  }
}

function setFailureState(dbModule, state) {
  try { dbModule?.setSetting?.(MCP_FAILURE_SETTING, state || {}); } catch {}
}

function recordCodexMcpFailuresFromOutput(data, options = {}) {
  const failures = extractCodexMcpFailures(data);
  if (!failures.length || !options.dbModule) return failures;
  const now = options.now || Date.now();
  const state = getFailureState(options.dbModule);
  for (const failure of failures) {
    state[failure.server] = {
      server: failure.server,
      reason: failure.reason,
      sample: failure.sample,
      lastSeenAt: now,
    };
  }
  setFailureState(options.dbModule, pruneFailureState(state, now, options.ttlMs || MCP_FAILURE_TTL_MS));
  return failures;
}

function pruneFailureState(state, now = Date.now(), ttlMs = MCP_FAILURE_TTL_MS) {
  const out = {};
  for (const [server, failure] of Object.entries(state || {})) {
    const lastSeenAt = Number(failure?.lastSeenAt || 0);
    if (!lastSeenAt || now - lastSeenAt > ttlMs) continue;
    out[server] = failure;
  }
  return out;
}

function isOptionalRemoteMcpServer(server) {
  if (!server || PROTECTED_MCP_SERVERS.has(String(server.name || '').toLowerCase())) return false;
  return server.enabled !== false && server.transport === 'http';
}

function shouldAutoDisableFailure(failure) {
  return ['not_logged_in', 'oauth_token_refresh_failed', 'auth_failed', 'missing_env'].includes(String(failure?.reason || ''));
}

function tomlBareKey(value) {
  const text = String(value || '');
  return /^[A-Za-z0-9_-]+$/.test(text)
    ? text
    : JSON.stringify(text);
}

function codexProjectTrustConfigArg(projectPath) {
  const resolved = path.resolve(String(projectPath || '').trim());
  return `projects.${JSON.stringify(resolved)}.trust_level="trusted"`;
}

function isProjectTrustDisabled(env = process.env) {
  const value = String(env?.[PROJECT_TRUST_ENV] || process.env[PROJECT_TRUST_ENV] || '').trim().toLowerCase();
  return value === '0' || value === 'false' || value === 'no' || value === 'off';
}

function isCodexHttpTransportEnabled(env = process.env) {
  const value = String(env?.[HTTP_TRANSPORT_ENV] || process.env[HTTP_TRANSPORT_ENV] || '').trim().toLowerCase();
  return value === '1' || value === 'true' || value === 'yes' || value === 'on';
}

function codexHttpTransportConfigArgs(providerId = HTTP_TRANSPORT_PROVIDER_ID) {
  const key = tomlBareKey(providerId || HTTP_TRANSPORT_PROVIDER_ID);
  return [
    '-c', `model_provider=${JSON.stringify(providerId || HTTP_TRANSPORT_PROVIDER_ID)}`,
    '-c', `model_providers.${key}.name="OpenAI HTTPS (CTM)"`,
    '-c', `model_providers.${key}.base_url="https://api.openai.com/v1"`,
    '-c', `model_providers.${key}.requires_openai_auth=true`,
    '-c', `model_providers.${key}.supports_websockets=false`,
    '-c', `model_providers.${key}.stream_max_retries=8`,
  ];
}

function argsContainCodexModelProviderOverride(args) {
  const values = Array.isArray(args) ? args : [];
  for (let i = 0; i < values.length; i += 1) {
    const current = String(values[i] || '');
    const next = String(values[i + 1] || '');
    if ((current === '-c' || current === '--config') &&
        (/^model_provider\s*=/.test(next) || /^model_providers\./.test(next))) {
      return true;
    }
    if (/^--config=/.test(current)) {
      const value = current.slice('--config='.length);
      if (/^model_provider\s*=/.test(value) || /^model_providers\./.test(value)) return true;
    }
    if (/^model_provider\s*=/.test(current) || /^model_providers\./.test(current)) return true;
  }
  return false;
}

function argsUseOssProvider(args) {
  return (Array.isArray(args) ? args : []).some((arg) => {
    const text = String(arg || '');
    return text === '--oss' || text === '--local-provider' || text.startsWith('--local-provider=');
  });
}

function applyCodexHttpTransportOverride(args, options = {}) {
  const originalArgs = Array.isArray(args) ? args.slice() : [];
  if (!isCodexHttpTransportEnabled(options.env)) {
    return { args: originalArgs, added: false, skipped: 'disabled_by_default' };
  }
  if (argsUseOssProvider(originalArgs)) {
    return { args: originalArgs, added: false, skipped: 'oss_provider' };
  }
  if (argsContainCodexModelProviderOverride(originalArgs)) {
    return { args: originalArgs, added: false, skipped: 'already_configured' };
  }
  const providerId = String(options.httpTransportProviderId || HTTP_TRANSPORT_PROVIDER_ID).trim() || HTTP_TRANSPORT_PROVIDER_ID;
  return {
    args: [...codexHttpTransportConfigArgs(providerId), ...originalArgs],
    added: true,
    providerId,
  };
}

function argsContainProjectTrustOverride(args, projectPath) {
  const resolved = path.resolve(String(projectPath || '').trim());
  const quoted = JSON.stringify(resolved);
  return (args || []).some((arg) => {
    const text = String(arg || '');
    if (!text.includes('projects.') || !text.includes('trust_level')) return false;
    return text.includes(quoted) || text.includes(resolved);
  });
}

function applyProjectTrustOverride(args, options = {}) {
  const originalArgs = Array.isArray(args) ? args.slice() : [];
  if (isProjectTrustDisabled(options.env)) {
    return { args: originalArgs, added: false, skipped: 'disabled_by_env' };
  }
  const cwd = String(options.cwd || options.projectPath || '').trim();
  if (!cwd) return { args: originalArgs, added: false, skipped: 'missing_cwd' };
  if (!fs.existsSync(cwd)) return { args: originalArgs, added: false, skipped: 'cwd_missing' };
  if (argsContainProjectTrustOverride(originalArgs, cwd)) {
    return { args: originalArgs, added: false, skipped: 'already_configured' };
  }
  return {
    args: ['-c', codexProjectTrustConfigArg(cwd), ...originalArgs],
    added: true,
    projectPath: path.resolve(cwd),
  };
}

function argsContainMcpOverride(args, serverName) {
  const needle1 = `mcp_servers.${serverName}.enabled`;
  const needle2 = `mcp_servers.${JSON.stringify(serverName)}.enabled`;
  return (args || []).some((arg) => {
    const text = String(arg || '');
    return text.includes(needle1) || text.includes(needle2);
  });
}

function applyLearnedMcpFailureOverrides(args, options = {}) {
  const originalArgs = Array.isArray(args) ? args.slice() : [];
  if (String(options.env?.CTM_CODEX_MCP_AUTO_DISABLE || process.env.CTM_CODEX_MCP_AUTO_DISABLE || '').trim() === '0') {
    return { args: originalArgs, disabled: [], skipped: 'disabled_by_env' };
  }
  const now = options.now || Date.now();
  const failures = pruneFailureState(getFailureState(options.dbModule), now, options.ttlMs || MCP_FAILURE_TTL_MS);
  const servers = new Map(readCodexMcpServers(options).map((server) => [server.name, server]));
  const disabled = [];
  const overrides = [];
  for (const [name, failure] of Object.entries(failures)) {
    const server = servers.get(name);
    if (!isOptionalRemoteMcpServer(server) || !shouldAutoDisableFailure(failure)) continue;
    if (argsContainMcpOverride(originalArgs, name)) continue;
    overrides.push('-c', `mcp_servers.${tomlBareKey(name)}.enabled=false`);
    disabled.push({ server: name, reason: failure.reason });
  }
  return { args: [...overrides, ...originalArgs], disabled };
}

function missingRequiredMcpEnv(server, env = process.env) {
  const key = String(server?.bearerTokenEnvVar || '').trim();
  if (!key) return '';
  return String(env?.[key] || process.env[key] || '').trim() ? '' : key;
}

function applyMissingMcpEnvOverrides(args, options = {}) {
  const originalArgs = Array.isArray(args) ? args.slice() : [];
  if (String(options.env?.CTM_CODEX_MCP_AUTO_DISABLE || process.env.CTM_CODEX_MCP_AUTO_DISABLE || '').trim() === '0') {
    return { args: originalArgs, disabled: [], skipped: 'disabled_by_env' };
  }
  const disabled = [];
  const overrides = [];
  for (const server of readCodexMcpServers(options)) {
    if (!isOptionalRemoteMcpServer(server)) continue;
    const missingEnv = missingRequiredMcpEnv(server, options.env);
    if (!missingEnv) continue;
    if (argsContainMcpOverride(originalArgs, server.name)) continue;
    overrides.push('-c', `mcp_servers.${tomlBareKey(server.name)}.enabled=false`);
    disabled.push({ server: server.name, reason: 'missing_env', env: missingEnv });
  }
  return { args: [...overrides, ...originalArgs], disabled };
}

function parseSkillMd(content) {
  const match = String(content || '').match(/^(---[^\S\r\n]*(?:\r?\n))([\s\S]*?)(\r?\n---[^\S\r\n]*(?:\r?\n|$))/);
  if (!match) return { ok: false, reason: 'missing_frontmatter' };
  return {
    ok: true,
    frontmatter: match[2],
    bodyOffset: match[0].length,
    opening: match[1],
    closing: match[3],
  };
}

function parseSkillDescription(frontmatter) {
  const lines = String(frontmatter || '').split(/\r?\n/);
  for (let i = 0; i < lines.length; i += 1) {
    const match = lines[i].match(/^description:\s*(.*)$/);
    if (!match) continue;
    const rest = match[1].trim();
    if (/^[>|]/.test(rest)) {
      const block = [];
      for (let j = i + 1; j < lines.length; j += 1) {
        if (lines[j].trim() && !/^\s+/.test(lines[j])) break;
        const value = lines[j].replace(/^\s+/, '');
        if (value) block.push(value);
      }
      return block.join(rest.startsWith('>') ? ' ' : '\n').replace(/\s+/g, ' ').trim();
    }
    return rest.replace(/^['"]|['"]$/g, '').replace(/\s+/g, ' ').trim();
  }
  return '';
}

function shortenDescription(value, limit = SKILL_DESCRIPTION_REPAIR_TARGET) {
  const oneLine = String(value || '').replace(/\s+/g, ' ').trim();
  if (oneLine.length <= limit) return oneLine;
  const slice = oneLine.slice(0, Math.max(0, limit - 1));
  const boundary = slice.lastIndexOf(' ');
  return `${(boundary > 200 ? slice.slice(0, boundary) : slice).trim()}...`;
}

function yamlDoubleQuoted(value) {
  return `"${String(value || '').replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`;
}

function replaceSkillDescription(content, nextDescription) {
  const parsed = parseSkillMd(content);
  if (!parsed.ok) return null;
  const frontmatter = parsed.frontmatter;
  const body = content.slice(parsed.bodyOffset);
  const lines = frontmatter.split(/\r?\n/);
  const out = [];
  for (let i = 0; i < lines.length; i += 1) {
    const line = lines[i];
    const match = line.match(/^description:\s*(.*)$/);
    if (!match) {
      out.push(line);
      continue;
    }
    out.push(`description: ${yamlDoubleQuoted(nextDescription)}`);
    const rest = match[1].trim();
    if (/^[>|]/.test(rest)) {
      while (i + 1 < lines.length && (!lines[i + 1].trim() || /^\s+/.test(lines[i + 1]))) i += 1;
    }
  }
  return parsed.opening + out.join('\n') + parsed.closing + body;
}

function findSkillFiles(root, options = {}) {
  const maxFiles = options.maxFiles || 5000;
  const out = [];
  const stack = [root];
  const seen = new Set();
  while (stack.length && out.length < maxFiles) {
    const dir = stack.pop();
    let real = '';
    try { real = fs.realpathSync(dir); } catch { continue; }
    if (seen.has(real)) continue;
    seen.add(real);
    let entries;
    try { entries = fs.readdirSync(dir, { withFileTypes: true }); } catch { continue; }
    for (const entry of entries) {
      if (entry.name === 'node_modules' || entry.name.startsWith('.')) continue;
      const full = path.join(dir, entry.name);
      if (entry.isDirectory()) stack.push(full);
      else if (entry.isFile() && entry.name === 'SKILL.md') out.push(full);
      if (out.length >= maxFiles) break;
    }
  }
  return out;
}

function realpathOrSelf(filePath) {
  try { return fs.realpathSync(filePath); } catch { return String(filePath || ''); }
}

function uniqueExistingSkillRoots(roots) {
  const out = [];
  const seen = new Set();
  for (const root of roots || []) {
    if (!root) continue;
    const real = realpathOrSelf(root);
    if (!real || seen.has(real)) continue;
    try { fs.statSync(root); } catch { continue; }
    seen.add(real);
    out.push(root);
  }
  return out;
}

function codexSkillRepairRoots(options = {}) {
  if (options.cacheRoot) return uniqueExistingSkillRoots([options.cacheRoot]);
  const home = codexHome(options);
  return uniqueExistingSkillRoots([
    path.join(home, 'plugins', 'cache'),
    path.join(home, '.tmp', 'plugins'),
  ]);
}

function skillRootSignature(root, options = {}) {
  const files = findSkillFiles(root, options);
  let newest = 0;
  const sample = [];
  for (const filePath of files) {
    let stat;
    try { stat = fs.statSync(filePath); } catch { continue; }
    newest = Math.max(newest, Math.floor(stat.mtimeMs || 0));
    if (sample.length < 120) {
      sample.push(`${path.relative(root, filePath)}:${Math.floor(stat.mtimeMs || 0)}:${stat.size || 0}`);
    }
  }
  return `${realpathOrSelf(root)}:${files.length}:${newest}:${sample.sort().join('|')}`;
}

// The signature walk (findSkillFiles + statSync over the whole skill tree) runs per codex
// launch to DECIDE whether a repair is due. During the restore storm the CALLER passes
// skipSkillRepair so prepareCodexLaunch never reaches here — that whole-tree walk was a
// profiled ~12s boot freeze (realpathSync/readdirSync, off-CPU so it shows as idle).
function skillRepairScanSignature(options = {}) {
  const roots = codexSkillRepairRoots(options);
  const sig = roots.map(root => skillRootSignature(root, options)).sort().join('\n');
  return sig;
}

function validateAndRepairCodexSkillCache(options = {}) {
  if (String(options.env?.CTM_CODEX_SKILL_REPAIR || process.env.CTM_CODEX_SKILL_REPAIR || '').trim() === '0') {
    return { scanned: 0, repaired: [], invalid: [], skipped: 'disabled_by_env' };
  }
  const report = { scanned: 0, repaired: [], invalid: [] };
  for (const cacheRoot of codexSkillRepairRoots(options)) {
    for (const filePath of findSkillFiles(cacheRoot, options)) {
      report.scanned += 1;
      let content = '';
      try { content = fs.readFileSync(filePath, 'utf8'); } catch { continue; }
      const parsed = parseSkillMd(content);
      if (!parsed.ok) {
        report.invalid.push({ filePath, reason: parsed.reason, repairable: false });
        continue;
      }
      const description = parseSkillDescription(parsed.frontmatter);
      if (!description) {
        report.invalid.push({ filePath, reason: 'missing_description', repairable: false });
        continue;
      }
      if (description.length <= (options.maxDescriptionLength || SKILL_DESCRIPTION_MAX)) continue;
      const shortened = shortenDescription(description, options.repairTargetLength || SKILL_DESCRIPTION_REPAIR_TARGET);
      const next = replaceSkillDescription(content, shortened);
      if (!next || next === content) {
        report.invalid.push({ filePath, reason: 'description_too_long', repairable: false, length: description.length });
        continue;
      }
      try {
        const backup = `${filePath}.ctm-bak`;
        if (!fs.existsSync(backup)) fs.writeFileSync(backup, content, 'utf8');
        fs.writeFileSync(filePath, next, 'utf8');
        report.repaired.push({ filePath, reason: 'description_too_long', from: description.length, to: shortened.length });
      } catch (err) {
        report.invalid.push({ filePath, reason: 'repair_failed', repairable: true, error: err.message, length: description.length });
      }
    }
  }
  return report;
}

let nextSkillRepairAt = 0;
let lastSkillRepairSignature = '';

function skillRepairRetryDelayMs(report, options = {}) {
  if (!report || report.skipped === 'disabled_by_env') return 0;
  const invalid = Array.isArray(report.invalid) ? report.invalid : [];
  if (!invalid.length) return Number.isFinite(options.minRepairIntervalMs) ? options.minRepairIntervalMs : 5 * 60 * 1000;
  const hasRepairableFailure = invalid.some(item => item && item.repairable !== false);
  if (!hasRepairableFailure) return Number.isFinite(options.minRepairIntervalMs) ? options.minRepairIntervalMs : 5 * 60 * 1000;
  return Number.isFinite(options.repairFailureRetryIntervalMs) ? options.repairFailureRetryIntervalMs : 15 * 1000;
}

function prepareCodexLaunch(options = {}) {
  const args = Array.isArray(options.args) ? options.args.slice() : [];
  if (!isCodexCommand(options.cmd)) {
    return {
      args,
      skillReport: null,
      mcpReport: { args, disabled: [] },
      projectTrustReport: { args, added: false, skipped: 'not_codex' },
      transportReport: { args, added: false, skipped: 'not_codex' },
    };
  }
  const now = options.now || Date.now();
  let skillReport = null;
  // The skill scan+repair is non-essential maintenance (it only shortens over-long SKILL.md
  // descriptions) — codex launches fine without it. During the boot/restore storm the caller
  // sets skipSkillRepair so we DON'T do the synchronous whole-tree walk per restored session;
  // it resumes on the next steady-state launch. This is the profiled 12s boot-freeze fix.
  if (!options.skipSkillRepair) {
    const minRepairIntervalMs = Number.isFinite(options.minRepairIntervalMs) ? options.minRepairIntervalMs : 5 * 60 * 1000;
    const repairSignature = skillRepairScanSignature(options);
    if (!nextSkillRepairAt || now >= nextSkillRepairAt || repairSignature !== lastSkillRepairSignature) {
      skillReport = validateAndRepairCodexSkillCache(options);
      const delay = skillRepairRetryDelayMs(skillReport, { ...options, minRepairIntervalMs });
      nextSkillRepairAt = delay > 0 ? now + delay : 0;
      lastSkillRepairSignature = repairSignature;
    }
  }
  const projectTrustReport = applyProjectTrustOverride(args, options);
  const transportReport = applyCodexHttpTransportOverride(projectTrustReport.args, options);
  const missingEnvReport = applyMissingMcpEnvOverrides(transportReport.args, options);
  const learnedMcpReport = applyLearnedMcpFailureOverrides(missingEnvReport.args, options);
  const mcpReport = {
    args: learnedMcpReport.args,
    disabled: [
      ...(missingEnvReport.disabled || []),
      ...(learnedMcpReport.disabled || []),
    ],
    missingEnv: missingEnvReport,
    learned: learnedMcpReport,
  };
  return { args: mcpReport.args, skillReport, mcpReport, projectTrustReport, transportReport };
}

function _resetCodexLaunchHealthState() {
  nextSkillRepairAt = 0;
  lastSkillRepairSignature = '';
}

module.exports = {
  MCP_FAILURE_SETTING,
  MCP_FAILURE_TTL_MS,
  applyLearnedMcpFailureOverrides,
  applyMissingMcpEnvOverrides,
  applyCodexHttpTransportOverride,
  extractCodexMcpFailures,
  parseCodexMcpServers,
  prepareCodexLaunch,
  recordCodexMcpFailuresFromOutput,
  validateAndRepairCodexSkillCache,
  _internals: {
    _resetCodexLaunchHealthState,
    argsContainCodexModelProviderOverride,
    applyCodexHttpTransportOverride,
    applyMissingMcpEnvOverrides,
    applyProjectTrustOverride,
    argsContainProjectTrustOverride,
    codexHttpTransportConfigArgs,
    codexSkillRepairRoots,
    codexHome,
    codexProjectTrustConfigArg,
    parseSkillDescription,
    parseSkillMd,
    replaceSkillDescription,
    sanitizeFailureSample,
    shortenDescription,
    skillRepairRetryDelayMs,
    skillRepairScanSignature,
  },
};
