From 96d51418d60684e704f9256ec115928551c10993 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Mon, 23 Feb 2026 02:43:08 +0900 Subject: [PATCH] refactor(hooks): remove dead hook files Delete 3 unused hook files: - hashline-edit-diff-enhancer/index.ts (and test file) - session-recovery/recover-empty-content-message.ts Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus --- .../hashline-edit-diff-enhancer/index.test.ts | 306 ------------------ .../hashline-edit-diff-enhancer/index.ts | 1 - .../recover-empty-content-message.ts | 87 ----- 3 files changed, 394 deletions(-) delete mode 100644 src/hooks/hashline-edit-diff-enhancer/index.test.ts delete mode 100644 src/hooks/hashline-edit-diff-enhancer/index.ts delete mode 100644 src/hooks/session-recovery/recover-empty-content-message.ts diff --git a/src/hooks/hashline-edit-diff-enhancer/index.test.ts b/src/hooks/hashline-edit-diff-enhancer/index.test.ts deleted file mode 100644 index ee39442e..00000000 --- a/src/hooks/hashline-edit-diff-enhancer/index.test.ts +++ /dev/null @@ -1,306 +0,0 @@ -import { describe, test, expect, beforeEach } from "bun:test" -import { createHashlineEditDiffEnhancerHook } from "./hook" - -function makeInput(tool: string, callID = "call-1", sessionID = "ses-1") { - return { tool, sessionID, callID } -} - -function makeBeforeOutput(args: Record) { - return { args } -} - -function makeAfterOutput(overrides?: Partial<{ title: string; output: string; metadata: Record }>) { - return { - title: overrides?.title ?? "", - output: overrides?.output ?? "Successfully applied 1 edit(s)", - metadata: overrides?.metadata ?? { truncated: false }, - } -} - -type FileDiffMetadata = { - file: string - path: string - before: string - after: string - additions: number - deletions: number -} - -describe("hashline-edit-diff-enhancer", () => { - let hook: ReturnType - - beforeEach(() => { - hook = createHashlineEditDiffEnhancerHook({ hashline_edit: { enabled: true } }) - }) - - describe("tool.execute.before", () => { - test("captures old file content for write tool", async () => { - const filePath = import.meta.dir + "/index.test.ts" - const input = makeInput("write") - const output = makeBeforeOutput({ path: filePath, edits: [] }) - - await hook["tool.execute.before"](input, output) - - // given the hook ran without error, the old content should be stored internally - // we verify in the after hook test that it produces filediff - }) - - test("ignores non-write tools", async () => { - const input = makeInput("read") - const output = makeBeforeOutput({ path: "/some/file.ts" }) - - // when - should not throw - await hook["tool.execute.before"](input, output) - }) - }) - - describe("tool.execute.after", () => { - test("injects filediff metadata after write tool execution", async () => { - // given - a temp file that we can modify between before/after - const tmpDir = (await import("os")).tmpdir() - const tmpFile = `${tmpDir}/hashline-diff-test-${Date.now()}.ts` - const oldContent = "line 1\nline 2\nline 3\n" - await Bun.write(tmpFile, oldContent) - - const input = makeInput("write", "call-diff-1") - const beforeOutput = makeBeforeOutput({ path: tmpFile, edits: [] }) - - // when - before hook captures old content - await hook["tool.execute.before"](input, beforeOutput) - - // when - file is modified (simulating write execution) - const newContent = "line 1\nmodified line 2\nline 3\nnew line 4\n" - await Bun.write(tmpFile, newContent) - - // when - after hook computes filediff - const afterOutput = makeAfterOutput() - await hook["tool.execute.after"](input, afterOutput) - - // then - metadata should contain filediff - const filediff = afterOutput.metadata.filediff as { - file: string - path: string - before: string - after: string - additions: number - deletions: number - } - expect(filediff).toBeDefined() - expect(filediff.file).toBe(tmpFile) - expect(filediff.path).toBe(tmpFile) - expect(filediff.before).toBe(oldContent) - expect(filediff.after).toBe(newContent) - expect(filediff.additions).toBeGreaterThan(0) - expect(filediff.deletions).toBeGreaterThan(0) - - // then - title should be set to the file path - expect(afterOutput.title).toBe(tmpFile) - - // cleanup - await Bun.file(tmpFile).exists() && (await import("fs/promises")).unlink(tmpFile) - }) - - test("does nothing for non-write tools", async () => { - const input = makeInput("read", "call-other") - const afterOutput = makeAfterOutput() - const originalMetadata = { ...afterOutput.metadata } - - await hook["tool.execute.after"](input, afterOutput) - - // then - metadata unchanged - expect(afterOutput.metadata).toEqual(originalMetadata) - }) - - test("does nothing when no before capture exists", async () => { - // given - no before hook was called for this callID - const input = makeInput("write", "call-no-before") - const afterOutput = makeAfterOutput() - const originalMetadata = { ...afterOutput.metadata } - - await hook["tool.execute.after"](input, afterOutput) - - // then - metadata unchanged (no filediff injected) - expect(afterOutput.metadata.filediff).toBeUndefined() - }) - - test("cleans up stored content after consumption", async () => { - const tmpDir = (await import("os")).tmpdir() - const tmpFile = `${tmpDir}/hashline-diff-cleanup-${Date.now()}.ts` - await Bun.write(tmpFile, "original") - - const input = makeInput("write", "call-cleanup") - await hook["tool.execute.before"](input, makeBeforeOutput({ path: tmpFile })) - await Bun.write(tmpFile, "modified") - - // when - first after call consumes - const afterOutput1 = makeAfterOutput() - await hook["tool.execute.after"](input, afterOutput1) - expect(afterOutput1.metadata.filediff).toBeDefined() - - // when - second after call finds nothing - const afterOutput2 = makeAfterOutput() - await hook["tool.execute.after"](input, afterOutput2) - expect(afterOutput2.metadata.filediff).toBeUndefined() - - await (await import("fs/promises")).unlink(tmpFile).catch(() => {}) - }) - - test("handles file creation (empty old content)", async () => { - const tmpDir = (await import("os")).tmpdir() - const tmpFile = `${tmpDir}/hashline-diff-create-${Date.now()}.ts` - - // given - file doesn't exist during before hook - const input = makeInput("write", "call-create") - await hook["tool.execute.before"](input, makeBeforeOutput({ path: tmpFile })) - - // when - file created during write - await Bun.write(tmpFile, "new content\n") - - const afterOutput = makeAfterOutput() - await hook["tool.execute.after"](input, afterOutput) - - // then - filediff shows creation (before is empty) - const filediff = afterOutput.metadata.filediff as FileDiffMetadata - expect(filediff).toBeDefined() - expect(filediff.before).toBe("") - expect(filediff.after).toBe("new content\n") - expect(filediff.additions).toBeGreaterThan(0) - expect(filediff.deletions).toBe(0) - - await (await import("fs/promises")).unlink(tmpFile).catch(() => {}) - }) - }) - - describe("disabled config", () => { - test("does nothing when hashline_edit is disabled", async () => { - const disabledHook = createHashlineEditDiffEnhancerHook({ hashline_edit: { enabled: false } }) - const tmpDir = (await import("os")).tmpdir() - const tmpFile = `${tmpDir}/hashline-diff-disabled-${Date.now()}.ts` - await Bun.write(tmpFile, "content") - - const input = makeInput("write", "call-disabled") - await disabledHook["tool.execute.before"](input, makeBeforeOutput({ path: tmpFile })) - await Bun.write(tmpFile, "modified") - - const afterOutput = makeAfterOutput() - await disabledHook["tool.execute.after"](input, afterOutput) - - // then - no filediff injected - expect(afterOutput.metadata.filediff).toBeUndefined() - - await (await import("fs/promises")).unlink(tmpFile).catch(() => {}) - }) - }) - - describe("write tool support", () => { - test("captures filediff for write tool (path arg)", async () => { - //#given - a temp file - const tmpDir = (await import("os")).tmpdir() - const tmpFile = `${tmpDir}/hashline-diff-write-${Date.now()}.ts` - const oldContent = "line 1\nline 2\n" - await Bun.write(tmpFile, oldContent) - - const input = makeInput("write", "call-write-1") - const beforeOutput = makeBeforeOutput({ path: tmpFile }) - - //#when - before hook captures old content - await hook["tool.execute.before"](input, beforeOutput) - - //#when - file is written - const newContent = "line 1\nmodified line 2\nnew line 3\n" - await Bun.write(tmpFile, newContent) - - //#when - after hook computes filediff - const afterOutput = makeAfterOutput() - await hook["tool.execute.after"](input, afterOutput) - - //#then - metadata should contain filediff - const filediff = afterOutput.metadata.filediff as { file: string; before: string; after: string; additions: number; deletions: number } - expect(filediff).toBeDefined() - expect(filediff.file).toBe(tmpFile) - expect(filediff.additions).toBeGreaterThan(0) - - await (await import("fs/promises")).unlink(tmpFile).catch(() => {}) - }) - - test("captures filediff for write tool (filePath arg)", async () => { - //#given - const tmpDir = (await import("os")).tmpdir() - const tmpFile = `${tmpDir}/hashline-diff-write-fp-${Date.now()}.ts` - await Bun.write(tmpFile, "original content\n") - - const input = makeInput("write", "call-write-fp") - - //#when - before hook uses filePath arg - await hook["tool.execute.before"](input, makeBeforeOutput({ filePath: tmpFile })) - await Bun.write(tmpFile, "new content\n") - - const afterOutput = makeAfterOutput() - await hook["tool.execute.after"](input, afterOutput) - - //#then - const filediff = afterOutput.metadata.filediff as FileDiffMetadata | undefined - expect(filediff).toBeDefined() - - await (await import("fs/promises")).unlink(tmpFile).catch(() => {}) - }) - }) - - describe("raw content in filediff", () => { - test("filediff.before and filediff.after are raw file content", async () => { - //#given - a temp file - const tmpDir = (await import("os")).tmpdir() - const tmpFile = `${tmpDir}/hashline-diff-format-${Date.now()}.ts` - const oldContent = "const x = 1\nconst y = 2\n" - await Bun.write(tmpFile, oldContent) - - const input = makeInput("write", "call-hashline-format") - await hook["tool.execute.before"](input, makeBeforeOutput({ path: tmpFile })) - - //#when - file is modified and after hook runs - const newContent = "const x = 1\nconst y = 42\n" - await Bun.write(tmpFile, newContent) - - const afterOutput = makeAfterOutput() - await hook["tool.execute.after"](input, afterOutput) - - //#then - before and after should be raw file content - const filediff = afterOutput.metadata.filediff as { before: string; after: string } - expect(filediff.before).toBe(oldContent) - expect(filediff.after).toBe(newContent) - - await (await import("fs/promises")).unlink(tmpFile).catch(() => {}) - }) - }) - - describe("TUI diff support (metadata.diff)", () => { - test("injects unified diff string in metadata.diff for write tool TUI", async () => { - //#given - a temp file - const tmpDir = (await import("os")).tmpdir() - const tmpFile = `${tmpDir}/hashline-tui-diff-${Date.now()}.ts` - const oldContent = "line 1\nline 2\nline 3\n" - await Bun.write(tmpFile, oldContent) - - const input = makeInput("write", "call-tui-diff") - await hook["tool.execute.before"](input, makeBeforeOutput({ path: tmpFile })) - - //#when - file is modified - const newContent = "line 1\nmodified line 2\nline 3\n" - await Bun.write(tmpFile, newContent) - - const afterOutput = makeAfterOutput() - await hook["tool.execute.after"](input, afterOutput) - - //#then - metadata.diff should be a unified diff string - expect(afterOutput.metadata.diff).toBeDefined() - expect(typeof afterOutput.metadata.diff).toBe("string") - expect(afterOutput.metadata.diff).toContain("---") - expect(afterOutput.metadata.diff).toContain("+++") - expect(afterOutput.metadata.diff).toContain("@@") - expect(afterOutput.metadata.diff).toContain("-line 2") - expect(afterOutput.metadata.diff).toContain("+modified line 2") - - await (await import("fs/promises")).unlink(tmpFile).catch(() => {}) - }) - }) -}) diff --git a/src/hooks/hashline-edit-diff-enhancer/index.ts b/src/hooks/hashline-edit-diff-enhancer/index.ts deleted file mode 100644 index 883bccfe..00000000 --- a/src/hooks/hashline-edit-diff-enhancer/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { createHashlineEditDiffEnhancerHook } from "./hook" diff --git a/src/hooks/session-recovery/recover-empty-content-message.ts b/src/hooks/session-recovery/recover-empty-content-message.ts deleted file mode 100644 index 7b73f34f..00000000 --- a/src/hooks/session-recovery/recover-empty-content-message.ts +++ /dev/null @@ -1,87 +0,0 @@ -import type { createOpencodeClient } from "@opencode-ai/sdk" -import type { MessageData } from "./types" -import { extractMessageIndex } from "./detect-error-type" -import { recoverEmptyContentMessageFromSDK } from "./recover-empty-content-message-sdk" -import { - findEmptyMessageByIndex, - findEmptyMessages, - findMessagesWithEmptyTextParts, - findMessagesWithThinkingOnly, - injectTextPart, - replaceEmptyTextParts, -} from "./storage" -import { isSqliteBackend } from "../../shared/opencode-storage-detection" -import { replaceEmptyTextPartsAsync, findMessagesWithEmptyTextPartsFromSDK } from "./storage/empty-text" -import { injectTextPartAsync } from "./storage/text-part-injector" - -type Client = ReturnType - -const PLACEHOLDER_TEXT = "[user interrupted]" - -export async function recoverEmptyContentMessage( - client: Client, - sessionID: string, - failedAssistantMsg: MessageData, - _directory: string, - error: unknown -): Promise { - if (isSqliteBackend()) { - return recoverEmptyContentMessageFromSDK(client, sessionID, failedAssistantMsg, error, { - placeholderText: PLACEHOLDER_TEXT, - replaceEmptyTextPartsAsync, - injectTextPartAsync, - findMessagesWithEmptyTextPartsFromSDK, - }) - } - - const targetIndex = extractMessageIndex(error) - const failedID = failedAssistantMsg.info?.id - let anySuccess = false - - const messagesWithEmptyText = findMessagesWithEmptyTextParts(sessionID) - for (const messageID of messagesWithEmptyText) { - if (replaceEmptyTextParts(messageID, PLACEHOLDER_TEXT)) { - anySuccess = true - } - } - - const thinkingOnlyIDs = findMessagesWithThinkingOnly(sessionID) - for (const messageID of thinkingOnlyIDs) { - if (injectTextPart(sessionID, messageID, PLACEHOLDER_TEXT)) { - anySuccess = true - } - } - - if (targetIndex !== null) { - const targetMessageID = findEmptyMessageByIndex(sessionID, targetIndex) - if (targetMessageID) { - if (replaceEmptyTextParts(targetMessageID, PLACEHOLDER_TEXT)) { - return true - } - if (injectTextPart(sessionID, targetMessageID, PLACEHOLDER_TEXT)) { - return true - } - } - } - - if (failedID) { - if (replaceEmptyTextParts(failedID, PLACEHOLDER_TEXT)) { - return true - } - if (injectTextPart(sessionID, failedID, PLACEHOLDER_TEXT)) { - return true - } - } - - const emptyMessageIDs = findEmptyMessages(sessionID) - for (const messageID of emptyMessageIDs) { - if (replaceEmptyTextParts(messageID, PLACEHOLDER_TEXT)) { - anySuccess = true - } - if (injectTextPart(sessionID, messageID, PLACEHOLDER_TEXT)) { - anySuccess = true - } - } - - return anySuccess -}