Files
llmx/codex-cli/src/parse-apply-patch.ts
Michael f4b9153f78 chore: consolidate patch prefix constants in apply‑patch.ts (#274)
This PR replaces all hard‑coded patch markers in apply‑patch.ts with the
corresponding constants (now) exported from parse‑apply‑patch.ts.

Changes
• Import PATCH_PREFIX, PATCH_SUFFIX, ADD_FILE_PREFIX,
DELETE_FILE_PREFIX, UPDATE_FILE_PREFIX, MOVE_FILE_TO_PREFIX,
END_OF_FILE_PREFIX, and HUNK_ADD_LINE_PREFIX from parse‑apply‑patch.ts.
	•	Remove duplicate string literals for patch markers in apply‑patch.ts.
• Changed is_done() to trim the input to account for the slight
difference between the variables.

Why
• DRY & Consistency: Ensures a single source of truth for patch
prefixes.
• Maintainability: Simplifies future updates to prefix values by
centralizing them.
	•	Readability: Makes the code more declarative and self‑documenting.

All tests are passing, lint and format was ran.
2025-04-17 17:00:30 -07:00

114 lines
2.7 KiB
TypeScript

export type ApplyPatchCreateFileOp = {
type: "create";
path: string;
content: string;
};
export type ApplyPatchDeleteFileOp = {
type: "delete";
path: string;
};
export type ApplyPatchUpdateFileOp = {
type: "update";
path: string;
update: string;
added: number;
deleted: number;
};
export type ApplyPatchOp =
| ApplyPatchCreateFileOp
| ApplyPatchDeleteFileOp
| ApplyPatchUpdateFileOp;
export const PATCH_PREFIX = "*** Begin Patch\n";
export const PATCH_SUFFIX = "\n*** End Patch";
export const ADD_FILE_PREFIX = "*** Add File: ";
export const DELETE_FILE_PREFIX = "*** Delete File: ";
export const UPDATE_FILE_PREFIX = "*** Update File: ";
export const MOVE_FILE_TO_PREFIX = "*** Move to: ";
export const END_OF_FILE_PREFIX = "*** End of File";
export const HUNK_ADD_LINE_PREFIX = "+";
/**
* @returns null when the patch is invalid
*/
export function parseApplyPatch(patch: string): Array<ApplyPatchOp> | null {
if (!patch.startsWith(PATCH_PREFIX)) {
// Patch must begin with '*** Begin Patch'
return null;
} else if (!patch.endsWith(PATCH_SUFFIX)) {
// Patch must end with '*** End Patch'
return null;
}
const patchBody = patch.slice(
PATCH_PREFIX.length,
patch.length - PATCH_SUFFIX.length,
);
const lines = patchBody.split("\n");
const ops: Array<ApplyPatchOp> = [];
for (const line of lines) {
if (line.startsWith(END_OF_FILE_PREFIX)) {
continue;
} else if (line.startsWith(ADD_FILE_PREFIX)) {
ops.push({
type: "create",
path: line.slice(ADD_FILE_PREFIX.length).trim(),
content: "",
});
continue;
} else if (line.startsWith(DELETE_FILE_PREFIX)) {
ops.push({
type: "delete",
path: line.slice(DELETE_FILE_PREFIX.length).trim(),
});
continue;
} else if (line.startsWith(UPDATE_FILE_PREFIX)) {
ops.push({
type: "update",
path: line.slice(UPDATE_FILE_PREFIX.length).trim(),
update: "",
added: 0,
deleted: 0,
});
continue;
}
const lastOp = ops[ops.length - 1];
if (lastOp?.type === "create") {
lastOp.content = appendLine(
lastOp.content,
line.slice(HUNK_ADD_LINE_PREFIX.length),
);
continue;
}
if (lastOp?.type !== "update") {
// Expected update op but got ${lastOp?.type} for line ${line}
return null;
}
if (line.startsWith(HUNK_ADD_LINE_PREFIX)) {
lastOp.added += 1;
} else if (line.startsWith("-")) {
lastOp.deleted += 1;
}
lastOp.update += lastOp.update ? "\n" + line : line;
}
return ops;
}
function appendLine(content: string, line: string) {
if (!content.length) {
return line;
}
return [content, line].join("\n");
}