import { hasConflictMarkers, splitLines } from "../git/merge3";
import { parseConflictText } from "~/lib/conflict-blocks";
import type { Segment } from "~/lib/conflict-blocks";
import type { ConflictHunk } from "../git/merge3";
import { scanText } from "../security/detectors";
export type CandidateSource = "ours" | "theirs" | "ours_then_theirs" | "theirs_then_ours" | "ai";
export type Candidate = { id: string; source: CandidateSource; label: string; text: string };
function terminate(text: string): string {
return text === "" || text.endsWith("\n") ? text : `${text}\n`;
}
/** The four deterministic resolutions, deduplicated by text so Clef never sees two identical options. */
export function baseCandidates(hunk: ConflictHunk): Candidate[] {
const ours = terminate(hunk.ours);
const theirs = terminate(hunk.theirs);
const all: Candidate[] = [
{ id: "ours", source: "ours", label: "Keep the head branch side only", text: ours },
{ id: "theirs", source: "theirs", label: "Keep the base branch side only", text: theirs },
{ id: "ours_then_theirs", source: "ours_then_theirs", label: "Head side, then base side", text: ours + theirs },
{ id: "theirs_then_ours", source: "theirs_then_ours", label: "Base side, then head side", text: theirs + ours },
];
return dedupe(all);
}
export function dedupe(candidates: Candidate[]): Candidate[] {
const seen = new Set<string>();
return candidates.filter((candidate) => {
if (seen.has(candidate.text)) return false;
seen.add(candidate.text);
return true;
});
}
/**
* Applies one chosen text per conflict block. Refuses when the parsed blocks do not line up with the
* merge's hunks (for example a file that already contained marker-like lines) or markers would remain.
*/
export function assembleResolution(content: string, hunks: ConflictHunk[], choices: string[]): string | null {