The instructions row of the agent detail panel gets an edit button that opens the agent's AGENTS.md, as the harness last published it, in a text box. Save sends a unified diff against the revision that was loaded; when the agent changed the file meanwhile the editor keeps the operator's text, shows the agent's version and offers to save over it or take it. An unsaved edit survives closing the dialog. The diff is built in the browser (LCS over lines, three lines of context, git's no-newline marker) and was checked against git apply on 600 random before/after pairs.
79 lines
2.5 KiB
JavaScript
79 lines
2.5 KiB
JavaScript
// The AGENTS.md editor's diff builder. Run by `npm test`, which Node runs with
|
|
// its built-in test runner and type stripping, so the `.ts` module is
|
|
// imported as it is. The expected strings are what `git diff` writes for the
|
|
// same two texts, and `git apply` reads back.
|
|
import assert from "node:assert/strict";
|
|
import { test } from "node:test";
|
|
import { unifiedDiff } from "../src/agent/detail/unified-diff.ts";
|
|
|
|
test("the same text has no diff", () => {
|
|
assert.equal(unifiedDiff("a\nb\n", "a\nb\n"), "");
|
|
assert.equal(unifiedDiff("", ""), "");
|
|
});
|
|
|
|
test("a changed middle line carries three lines of context", () => {
|
|
const before = "1\n2\n3\n4\n5\n6\n7\n8\n9\n";
|
|
const after = "1\n2\n3\n4\nfive\n6\n7\n8\n9\n";
|
|
assert.equal(
|
|
unifiedDiff(before, after),
|
|
"--- a/AGENTS.md\n+++ b/AGENTS.md\n" +
|
|
"@@ -2,7 +2,7 @@\n 2\n 3\n 4\n-5\n+five\n 6\n 7\n 8\n",
|
|
);
|
|
});
|
|
|
|
test("two far apart changes make two hunks, near ones merge", () => {
|
|
const lines = Array.from({ length: 20 }, (_, i) => `l${i + 1}`);
|
|
const far = [...lines];
|
|
far[1] = "X";
|
|
far[17] = "Y";
|
|
const farDiff = unifiedDiff(lines.join("\n") + "\n", far.join("\n") + "\n");
|
|
assert.equal(farDiff.match(/^@@/gm)?.length, 2);
|
|
|
|
const near = [...lines];
|
|
near[1] = "X";
|
|
near[6] = "Y";
|
|
const nearDiff = unifiedDiff(lines.join("\n") + "\n", near.join("\n") + "\n");
|
|
assert.equal(nearDiff.match(/^@@/gm)?.length, 1);
|
|
});
|
|
|
|
test("writing into an empty file adds every line", () => {
|
|
assert.equal(
|
|
unifiedDiff("", "hello\nworld\n"),
|
|
"--- a/AGENTS.md\n+++ b/AGENTS.md\n@@ -0,0 +1,2 @@\n+hello\n+world\n",
|
|
);
|
|
});
|
|
|
|
test("emptying a file removes every line", () => {
|
|
assert.equal(
|
|
unifiedDiff("hello\n", ""),
|
|
"--- a/AGENTS.md\n+++ b/AGENTS.md\n@@ -1 +0,0 @@\n-hello\n",
|
|
);
|
|
});
|
|
|
|
test("a missing final newline is marked as git marks it", () => {
|
|
assert.equal(
|
|
unifiedDiff("a\nb", "a\nb\n"),
|
|
"--- a/AGENTS.md\n+++ b/AGENTS.md\n@@ -1,2 +1,2 @@\n a\n" +
|
|
"-b\n\\ No newline at end of file\n+b\n",
|
|
);
|
|
assert.equal(
|
|
unifiedDiff("a\nb\n", "a\nc"),
|
|
"--- a/AGENTS.md\n+++ b/AGENTS.md\n@@ -1,2 +1,2 @@\n a\n" +
|
|
"-b\n+c\n\\ No newline at end of file\n",
|
|
);
|
|
});
|
|
|
|
test("a pure insertion keeps the lines around it as context", () => {
|
|
assert.equal(
|
|
unifiedDiff("a\nc\n", "a\nb\nc\n"),
|
|
"--- a/AGENTS.md\n+++ b/AGENTS.md\n@@ -1,2 +1,3 @@\n a\n+b\n c\n",
|
|
);
|
|
});
|
|
|
|
test("the file name is the one asked for", () => {
|
|
assert.ok(
|
|
unifiedDiff("a\n", "b\n", "CLAUDE.md").startsWith(
|
|
"--- a/CLAUDE.md\n+++ b/CLAUDE.md\n",
|
|
),
|
|
);
|
|
});
|