59 lines · 2.5 KB
import { splitLines } from "../git/merge3";
import { hunkQuestionId } from "./decide";
import type { ClefRequest, ClefResponse } from "./decide";
import type { MergeFixProvider } from "./plan";

const SECURITY_HINT = /password|secret|token|auth|crypto|permission/i;

/** Union of both sides' lines, keeping head order first: a plausible deterministic "AI" resolution. */
function unionLines(ours: string, theirs: string): string {
  const lines = splitLines(ours);
  const seen = new Set(lines);
  for (const line of splitLines(theirs)) if (!seen.has(line)) lines.push(line);
  return lines.join("");
}

function spread(ids: string[], pick: string, top: number): Record<string, number> {
  const rest = ids.length > 1 ? (1 - top) / (ids.length - 1) : 0;
  return Object.fromEntries(ids.map((id) => [id, id === pick ? top : rest]));
}

/**
 * Deterministic stand-in for the generative model and Clef, selected with MERGEFIX_PROVIDER=mock. It picks
 * the first AI candidate with high confidence and flags anything that mentions credentials as security-sensitive.
 */
export function mockProvider(): MergeFixProvider {
  return {
    async generate({ hunk }) {
      return {
        text: `\`\`\`merge\n${unionLines(hunk.ours, hunk.theirs)}\`\`\``,
        usage: { inputTokens: 100, outputTokens: 20 },
      };
    },
    async decide(request: ClefRequest): Promise<ClefResponse> {
      const answers: ClefResponse["answers"] = {};
      for (const [id, question] of Object.entries(request.questions)) {
        if (question.type !== "choice") continue;
        const ids = Object.keys(question.criteria);
        const pick =
          ids.find((option) => option.startsWith("ai_")) ??
          ids.find((option) => option === "ours_then_theirs") ??