From c781ef420ca5581c06b7b9d72dee095d211fd002 Mon Sep 17 00:00:00 2001 From: Kit Langton Date: Mon, 20 Jul 2026 14:16:27 -0400 Subject: [PATCH] fix(core): preserve patch receipt metadata --- packages/core/src/tool/edit.ts | 9 ++++----- packages/core/src/tool/patch.ts | 11 +++++------ packages/core/test/tool-edit.test.ts | 3 +-- packages/core/test/tool-patch.test.ts | 6 +++--- 4 files changed, 13 insertions(+), 16 deletions(-) diff --git a/packages/core/src/tool/edit.ts b/packages/core/src/tool/edit.ts index 5d0f9c6c0b..2319d4baf2 100644 --- a/packages/core/src/tool/edit.ts +++ b/packages/core/src/tool/edit.ts @@ -9,8 +9,8 @@ export * as EditTool from "./edit" import type { Context as PluginContext } from "@opencode-ai/plugin/v2/effect/plugin" import { ToolFailure } from "@opencode-ai/ai" import { FileDiff } from "@opencode-ai/schema/file-diff" -import { diffLines } from "diff" -import { Effect, Schema, Struct } from "effect" +import { createTwoFilesPatch, diffLines } from "diff" +import { Effect, Schema } from "effect" import { FileMutation } from "../file-mutation" import { FSUtil } from "../fs-util" import { LocationMutation } from "../location-mutation" @@ -31,10 +31,8 @@ export const Input = Schema.Struct({ }), }) -const FileInfo = FileDiff.Info.mapFields(Struct.omit(["patch"])) - export const Output = Schema.Struct({ - files: Schema.Array(FileInfo), + files: Schema.Array(FileDiff.Info), replacements: Schema.Number, }) export type Output = typeof Output.Type @@ -202,6 +200,7 @@ export const Plugin = { files: [ { file: result.resource, + patch: createTwoFilesPatch(result.resource, result.resource, source.text, replaced), status: "modified" as const, ...counts, }, diff --git a/packages/core/src/tool/patch.ts b/packages/core/src/tool/patch.ts index fadbde3174..6925b87793 100644 --- a/packages/core/src/tool/patch.ts +++ b/packages/core/src/tool/patch.ts @@ -3,8 +3,8 @@ export * as PatchTool from "./patch" import type { Context as PluginContext } from "@opencode-ai/plugin/v2/effect/plugin" import { ToolFailure } from "@opencode-ai/ai" import { FileDiff } from "@opencode-ai/schema/file-diff" -import { diffLines } from "diff" -import { Effect, Schema, Struct } from "effect" +import { createTwoFilesPatch, diffLines } from "diff" +import { Effect, Schema } from "effect" import { FileMutation } from "../file-mutation" import { FSUtil } from "../fs-util" import { LocationMutation } from "../location-mutation" @@ -26,11 +26,9 @@ export const Applied = Schema.Struct({ target: Schema.String, }) -const FileInfo = FileDiff.Info.mapFields(Struct.omit(["patch"])) - export const Output = Schema.Struct({ applied: Schema.Array(Applied), - files: Schema.Array(FileInfo), + files: Schema.Array(FileDiff.Info), }) export type Output = typeof Output.Type @@ -213,7 +211,7 @@ export const Plugin = { }), } -function patchFile(change: Prepared): typeof FileInfo.Type { +function patchFile(change: Prepared): typeof FileDiff.Info.Type { const counts = diffLines(change.before, change.after).reduce( (result, item) => ({ additions: result.additions + (item.added ? (item.count ?? 0) : 0), @@ -223,6 +221,7 @@ function patchFile(change: Prepared): typeof FileInfo.Type { ) return { file: change.target.resource, + patch: createTwoFilesPatch(change.target.resource, change.target.resource, change.before, change.after), status: change.type === "add" ? "added" : change.type === "delete" ? "deleted" : "modified", ...counts, } diff --git a/packages/core/test/tool-edit.test.ts b/packages/core/test/tool-edit.test.ts index 5b519038ee..ded236e654 100644 --- a/packages/core/test/tool-edit.test.ts +++ b/packages/core/test/tool-edit.test.ts @@ -158,10 +158,10 @@ describe("EditTool", () => { status: "modified", additions: 1, deletions: 1, + patch: expect.stringContaining("-before\n+after"), }, ], }) - expect(settled.output?.structured).not.toHaveProperty("files.0.patch") expect(yield* Effect.promise(() => fs.readFile(target, "utf8"))).toBe("after\nrest\n") expect(assertions).toMatchObject([{ sessionID, action: "edit", resources: ["hello.txt"], save: ["*"] }]) expect(writes).toEqual([yield* Effect.promise(() => fs.realpath(target))]) @@ -366,7 +366,6 @@ describe("EditTool", () => { Effect.andThen((settled) => Effect.gen(function* () { expect(settled.output?.structured).toMatchObject({ replacements: 3 }) - expect(settled.output?.structured).not.toHaveProperty("files.0.patch") expect(yield* Effect.promise(() => fs.readFile(target, "utf8"))).toBe("after after after") expect(writes).toHaveLength(1) }), diff --git a/packages/core/test/tool-patch.test.ts b/packages/core/test/tool-patch.test.ts index 7a4136e7f2..458f8c3758 100644 --- a/packages/core/test/tool-patch.test.ts +++ b/packages/core/test/tool-patch.test.ts @@ -181,24 +181,24 @@ describe("PatchTool", () => { status: "added", additions: 1, deletions: 0, + patch: expect.stringContaining("+created"), }, { file: "update.txt", status: "modified", additions: 1, deletions: 1, + patch: expect.stringContaining("-before\n+after"), }, { file: "remove.txt", status: "deleted", additions: 0, deletions: 1, + patch: expect.stringContaining("-remove"), }, ], }) - expect(settled.output?.structured).not.toHaveProperty("files.0.patch") - expect(settled.output?.structured).not.toHaveProperty("files.1.patch") - expect(settled.output?.structured).not.toHaveProperty("files.2.patch") expect(assertions).toMatchObject([ { sessionID, action: "edit", resources: ["nested/new.txt", "update.txt", "remove.txt"], save: ["*"] }, ])