// conformance/index.ts
import { spawnSync } from "child_process";
import { cpSync, existsSync, mkdtempSync, readdirSync, readFileSync, rmSync, statSync } from "fs";
import { tmpdir } from "os";
import { isAbsolute, join as join3, relative, resolve as resolve2 } from "path";

// kernel/util/format-error.ts
function formatErrorMessage(err) {
  return err instanceof Error ? err.message : String(err);
}

// kernel/util/skill-map-paths.ts
import { join as join2 } from "path";

// core/paths/db-path.ts
import { join, resolve } from "path";
var SKILL_MAP_DIR = ".skill-map";
var DB_FILENAME = "skill-map.db";
var LOCAL_SETTINGS_FILENAME = "settings.local.json";
var DEFAULT_DB_REL = `${SKILL_MAP_DIR}/${DB_FILENAME}`;
var GITIGNORE_ENTRIES = [
  `${SKILL_MAP_DIR}/${LOCAL_SETTINGS_FILENAME}`,
  `${SKILL_MAP_DIR}/${DB_FILENAME}`
];

// kernel/util/skill-map-paths.ts
var KERNEL_SKILL_MAP_DIR = SKILL_MAP_DIR;

// kernel/util/tx.ts
var TOKEN_RE = /\{\{\s*([A-Za-z][A-Za-z0-9_]*)\s*\}\}/g;
function tx(template, vars = {}) {
  return template.replace(TOKEN_RE, (_match, name) => {
    if (!Object.prototype.hasOwnProperty.call(vars, name)) {
      throw new Error(
        `tx: missing variable "${name}" for template "${template.slice(0, 80)}${template.length > 80 ? "\u2026" : ""}"`
      );
    }
    const value = vars[name];
    if (value === null || value === void 0) {
      throw new Error(
        `tx: variable "${name}" is null/undefined for template "${template.slice(0, 80)}${template.length > 80 ? "\u2026" : ""}"`
      );
    }
    return String(value);
  });
}

// conformance/i18n/runner.texts.ts
var CONFORMANCE_RUNNER_TEXTS = {
  priorScanFailed: "setup.priorScans step `{{fixture}}` failed with exit {{exit}}: {{stderr}}",
  pathMustBeRelative: 'conformance: {{label}} path "{{path}}" must be relative to its anchor ({{anchor}})',
  pathEscapesAnchor: 'conformance: {{label}} path "{{path}}" escapes its anchor ({{anchor}})',
  expectedExitCode: "expected exit {{expected}}, got {{actual}}",
  fileNotFound: "file not found: {{path}}",
  targetNotFound: "target not found: {{path}}",
  targetMissingFixture: "target does not contain fixture {{fixture}} verbatim",
  fileMatchesSchemaUnimplemented: "file-matches-schema not yet implemented (requires ajv; lands with Step 2)",
  stderrDidNotMatch: "stderr did not match /{{pattern}}/",
  stdoutNotJson: "stdout is not valid JSON: {{message}}",
  unsupportedJsonPath: "unsupported jsonpath: {{path}}",
  expectedArrayAtPath: "expected array at {{path}}",
  cannotTraverseSegment: "cannot traverse {{type}} at segment '{{segment}}'",
  jsonPathEqualsMismatch: "{{path}} = {{actual}}, expected {{expected}}",
  jsonPathNotGreaterThan: "{{path}} not > {{value}}",
  jsonPathNotLessThan: "{{path}} not < {{value}}",
  jsonPathDidNotMatch: "{{path}} did not match /{{pattern}}/",
  jsonPathNoComparator: "no comparator on json-path assertion",
  specRootMissingIndex: "spec root missing index.json at {{specRoot}}"
};

// conformance/index.ts
function disableEnv(setup) {
  const env = {};
  if (setup?.disableAllProviders) env["SKILL_MAP_DISABLE_ALL_PROVIDERS"] = "1";
  if (setup?.disableAllExtractors) env["SKILL_MAP_DISABLE_ALL_EXTRACTORS"] = "1";
  if (setup?.disableAllAnalyzers) env["SKILL_MAP_DISABLE_ALL_ANALYZERS"] = "1";
  return env;
}
function runConformanceCase(options) {
  const raw = readFileSync(options.casePath, "utf8");
  const c = JSON.parse(raw);
  const fixturesRoot = options.fixturesRoot ?? join3(options.specRoot, "conformance", "fixtures");
  const safeId = c.id.replace(/[^a-zA-Z0-9_-]/g, "_").slice(0, 32);
  const scope = mkdtempSync(join3(tmpdir(), `sm-conformance-${safeId}-`));
  const setupEnv = disableEnv(c.setup);
  try {
    const priorFailure = runPriorScansSetup(c, options, scope, fixturesRoot, setupEnv);
    if (priorFailure) return priorFailure;
    if (c.fixture) {
      replaceFixture(scope, fixturesRoot, c.fixture);
    }
    const argv = [c.invoke.verb];
    if (c.invoke.sub) argv.push(c.invoke.sub);
    if (c.invoke.args) argv.push(...c.invoke.args);
    if (c.invoke.flags) argv.push(...c.invoke.flags);
    const child = spawnSync(process.execPath, [options.binary, ...argv], {
      cwd: scope,
      env: { ...process.env, ...options.env, ...setupEnv },
      encoding: "utf8"
    });
    const stdout = child.stdout ?? "";
    const stderr = child.stderr ?? "";
    const exitCode = child.status ?? 0;
    const assertions = c.assertions.map(
      (a) => evaluateAssertion(a, {
        exitCode,
        stdout,
        stderr,
        scope,
        specRoot: options.specRoot,
        fixturesRoot
      })
    );
    const passed = assertions.every((a) => a.ok);
    return { caseId: c.id, passed, exitCode, stdout, stderr, assertions };
  } finally {
    rmSync(scope, { recursive: true, force: true });
  }
}
function runPriorScansSetup(c, options, scope, fixturesRoot, setupEnv) {
  for (const step of c.setup?.priorScans ?? []) {
    replaceFixture(scope, fixturesRoot, step.fixture);
    const stepArgv = ["scan", ...step.flags ?? []];
    const stepChild = spawnSync(process.execPath, [options.binary, ...stepArgv], {
      cwd: scope,
      env: { ...process.env, ...options.env, ...setupEnv },
      encoding: "utf8"
    });
    if ((stepChild.status ?? 0) !== 0) {
      return {
        caseId: c.id,
        passed: false,
        exitCode: stepChild.status ?? 0,
        stdout: stepChild.stdout ?? "",
        stderr: stepChild.stderr ?? "",
        assertions: [
          {
            ok: false,
            type: "priorScan",
            reason: tx(CONFORMANCE_RUNNER_TEXTS.priorScanFailed, {
              fixture: step.fixture,
              exit: stepChild.status ?? 0,
              stderr: stepChild.stderr ?? ""
            })
          }
        ]
      };
    }
  }
  return null;
}
function replaceFixture(scope, fixturesRoot, fixture) {
  assertContained(fixturesRoot, fixture, "fixture");
  for (const entry of readdirSync(scope)) {
    if (entry === KERNEL_SKILL_MAP_DIR) continue;
    rmSync(join3(scope, entry), { recursive: true, force: true });
  }
  const src = join3(fixturesRoot, fixture);
  cpSync(src, scope, { recursive: true });
}
function assertContained(root, rel, label) {
  if (isAbsolute(rel)) {
    throw new Error(
      tx(CONFORMANCE_RUNNER_TEXTS.pathMustBeRelative, { label, path: rel, anchor: root })
    );
  }
  const abs = resolve2(root, rel);
  const r = relative(root, abs);
  if (r.startsWith("..") || isAbsolute(r)) {
    throw new Error(
      tx(CONFORMANCE_RUNNER_TEXTS.pathEscapesAnchor, { label, path: rel, anchor: root })
    );
  }
}
function evaluateAssertion(a, ctx) {
  switch (a.type) {
    case "exit-code":
      return ctx.exitCode === a.value ? { ok: true, type: a.type } : {
        ok: false,
        type: a.type,
        reason: tx(CONFORMANCE_RUNNER_TEXTS.expectedExitCode, {
          expected: a.value,
          actual: ctx.exitCode
        })
      };
    case "json-path":
      return evaluateJsonPath(a, ctx);
    case "file-exists": {
      try {
        assertContained(ctx.scope, a.path, "file-exists");
      } catch (err) {
        return { ok: false, type: a.type, reason: formatErrorMessage(err) };
      }
      const abs = resolve2(ctx.scope, a.path);
      return existsSync(abs) ? { ok: true, type: a.type } : {
        ok: false,
        type: a.type,
        reason: tx(CONFORMANCE_RUNNER_TEXTS.fileNotFound, { path: a.path })
      };
    }
    case "file-contains-verbatim": {
      try {
        assertContained(ctx.fixturesRoot, a.fixture, "file-contains-verbatim/fixture");
        assertContained(ctx.scope, a.path, "file-contains-verbatim/path");
      } catch (err) {
        return { ok: false, type: a.type, reason: formatErrorMessage(err) };
      }
      const fixturePath = join3(ctx.fixturesRoot, a.fixture);
      const targetPath = resolve2(ctx.scope, a.path);
      if (!existsSync(targetPath)) {
        return {
          ok: false,
          type: a.type,
          reason: tx(CONFORMANCE_RUNNER_TEXTS.targetNotFound, { path: a.path })
        };
      }
      const needle = readFileSync(fixturePath);
      const haystack = readFileSync(targetPath);
      return haystack.includes(needle) ? { ok: true, type: a.type } : {
        ok: false,
        type: a.type,
        reason: tx(CONFORMANCE_RUNNER_TEXTS.targetMissingFixture, { fixture: a.fixture })
      };
    }
    case "file-matches-schema":
      return {
        ok: false,
        type: a.type,
        reason: CONFORMANCE_RUNNER_TEXTS.fileMatchesSchemaUnimplemented
      };
    case "stderr-matches": {
      const re = new RegExp(a.pattern);
      return re.test(ctx.stderr) ? { ok: true, type: a.type } : {
        ok: false,
        type: a.type,
        reason: tx(CONFORMANCE_RUNNER_TEXTS.stderrDidNotMatch, { pattern: a.pattern })
      };
    }
  }
}
function evaluateJsonPath(a, ctx) {
  let doc;
  try {
    doc = JSON.parse(ctx.stdout);
  } catch (err) {
    return {
      ok: false,
      type: a.type,
      reason: tx(CONFORMANCE_RUNNER_TEXTS.stdoutNotJson, { message: formatErrorMessage(err) })
    };
  }
  const segments = parsePath(a.path);
  if (!segments) {
    return {
      ok: false,
      type: a.type,
      reason: tx(CONFORMANCE_RUNNER_TEXTS.unsupportedJsonPath, { path: a.path })
    };
  }
  const walked = traverseJsonPath(doc, segments, a.path);
  if (!walked.ok) return { ok: false, type: a.type, reason: walked.reason };
  return applyJsonPathComparator(a, walked.value);
}
function traverseJsonPath(doc, segments, path) {
  let current = doc;
  for (const seg of segments) {
    if (typeof seg === "number") {
      if (!Array.isArray(current)) {
        return { ok: false, reason: tx(CONFORMANCE_RUNNER_TEXTS.expectedArrayAtPath, { path }) };
      }
      current = current[seg];
    } else if (seg === "length" && Array.isArray(current)) {
      current = current.length;
    } else if (typeof current === "object" && current !== null) {
      current = current[seg];
    } else {
      return {
        ok: false,
        reason: tx(CONFORMANCE_RUNNER_TEXTS.cannotTraverseSegment, {
          type: typeof current,
          segment: String(seg)
        })
      };
    }
  }
  return { ok: true, value: current };
}
function applyJsonPathComparator(a, current) {
  if ("equals" in a && a.equals !== void 0) {
    return deepEqual(current, a.equals) ? { ok: true, type: a.type } : {
      ok: false,
      type: a.type,
      reason: tx(CONFORMANCE_RUNNER_TEXTS.jsonPathEqualsMismatch, {
        path: a.path,
        actual: JSON.stringify(current),
        expected: JSON.stringify(a.equals)
      })
    };
  }
  if ("greaterThan" in a && typeof a.greaterThan === "number") {
    return typeof current === "number" && current > a.greaterThan ? { ok: true, type: a.type } : {
      ok: false,
      type: a.type,
      reason: tx(CONFORMANCE_RUNNER_TEXTS.jsonPathNotGreaterThan, {
        path: a.path,
        value: a.greaterThan
      })
    };
  }
  if ("lessThan" in a && typeof a.lessThan === "number") {
    return typeof current === "number" && current < a.lessThan ? { ok: true, type: a.type } : {
      ok: false,
      type: a.type,
      reason: tx(CONFORMANCE_RUNNER_TEXTS.jsonPathNotLessThan, {
        path: a.path,
        value: a.lessThan
      })
    };
  }
  if ("matches" in a && typeof a.matches === "string") {
    const re = new RegExp(a.matches);
    return typeof current === "string" && re.test(current) ? { ok: true, type: a.type } : {
      ok: false,
      type: a.type,
      reason: tx(CONFORMANCE_RUNNER_TEXTS.jsonPathDidNotMatch, {
        path: a.path,
        pattern: a.matches
      })
    };
  }
  return { ok: false, type: a.type, reason: CONFORMANCE_RUNNER_TEXTS.jsonPathNoComparator };
}
function parsePath(path) {
  if (!path.startsWith("$")) return null;
  const tail = path.slice(1);
  const segments = [];
  const re = /\.([a-zA-Z_][a-zA-Z0-9_-]*)|\[(\d+)\]/g;
  let lastIndex = 0;
  let match;
  while ((match = re.exec(tail)) !== null) {
    if (match.index !== lastIndex) return null;
    if (match[1] !== void 0) segments.push(match[1]);
    else if (match[2] !== void 0) segments.push(Number.parseInt(match[2], 10));
    lastIndex = re.lastIndex;
  }
  if (lastIndex !== tail.length) return null;
  return segments;
}
function deepEqual(a, b) {
  if (a === b) return true;
  if (typeof a !== typeof b) return false;
  if (a && b && typeof a === "object" && typeof b === "object") {
    if (Array.isArray(a) !== Array.isArray(b)) return false;
    const ak = Object.keys(a);
    const bk = Object.keys(b);
    if (ak.length !== bk.length) return false;
    for (const k of ak) {
      if (!deepEqual(
        a[k],
        b[k]
      )) {
        return false;
      }
    }
    return true;
  }
  return false;
}
function assertSpecRoot(specRoot) {
  const indexPath = join3(specRoot, "index.json");
  if (!existsSync(indexPath) || !statSync(indexPath).isFile()) {
    throw new Error(tx(CONFORMANCE_RUNNER_TEXTS.specRootMissingIndex, { specRoot }));
  }
}
export {
  assertSpecRoot,
  runConformanceCase
};
//# sourceMappingURL=index.js.map