fix(coding-agent): fixed edit pipeline silent-corruption paths

non-exact patch matches warn and prefix/substring matches must preserve the discarded suffix; multi-entry edits stop at first failure and report applied vs not; ast-edit and file-mention snapshots use canonical realpath keys and re-record post-apply; notebook marker-shaped lines escaped on render; fuzzy matcher pre-normalizes once per seek; streaming preview caches text+tree per tick.
This commit is contained in:
can1357
2026-06-10 01:27:39 +02:00
parent 255f49e265
commit b232e36628
10 changed files with 243 additions and 38 deletions
+22 -2
View File
@@ -21,6 +21,26 @@ export interface NotebookDocument {
}
const CELL_MARKER_RE = /^# %% \[(code|markdown|raw)\](?: cell:(\d+))?$/;
/**
* Cell source lines that would themselves parse as (possibly already-escaped)
* cell markers gain one extra `%` on render and lose it on parse, so a
* notebook that *contains* the literal text `# %% [markdown] cell:3` survives
* the editable-text round trip instead of being split into extra cells.
*/
const ESCAPABLE_MARKER_RE = /^# %%+ \[(?:code|markdown|raw)\](?: cell:\d+)?$/;
const ESCAPED_MARKER_RE = /^# %%%+ \[(?:code|markdown|raw)\](?: cell:\d+)?$/;
function escapeMarkerLikeSourceLines(source: string): string {
if (!source.includes("# %%")) return source;
return source
.split("\n")
.map(line => (ESCAPABLE_MARKER_RE.test(line) ? line.replace("# %", "# %%") : line))
.join("\n");
}
function unescapeMarkerLikeLine(line: string): string {
return ESCAPED_MARKER_RE.test(line) ? line.replace("# %%", "# %") : line;
}
export function isNotebookPath(filePath: string): boolean {
return path.extname(filePath).toLowerCase() === ".ipynb";
@@ -100,7 +120,7 @@ export async function readNotebookDocument(absolutePath: string, displayPath: st
export function notebookToEditableText(notebook: NotebookDocument): string {
return notebook.cells
.map((cell, index) => {
const source = sourceToText(cell.source);
const source = escapeMarkerLikeSourceLines(sourceToText(cell.source));
return source.length > 0
? `# %% [${cell.cell_type}] cell:${index}\n${source}`
: `# %% [${cell.cell_type}] cell:${index}`;
@@ -156,7 +176,7 @@ function parseNotebookEditableText(text: string, displayPath: string): ParsedVir
`Invalid notebook editable representation for ${displayPath}: expected first line to be "# %% [code] cell:0", "# %% [markdown] cell:0", or "# %% [raw] cell:0".`,
);
}
current.lines.push(line);
current.lines.push(unescapeMarkerLikeLine(line));
}
flush();
return cells;