From 3b39fe06f7e410ab00d9f6504218fa8252e7f933 Mon Sep 17 00:00:00 2001 From: LukeParkerDev <10430890+Hona@users.noreply.github.com> Date: Sun, 29 Mar 2026 13:24:57 +1000 Subject: [PATCH 1/2] test: add regression coverage for sync plugin hooks --- packages/opencode/test/plugin/trigger.test.ts | 131 ++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 packages/opencode/test/plugin/trigger.test.ts diff --git a/packages/opencode/test/plugin/trigger.test.ts b/packages/opencode/test/plugin/trigger.test.ts new file mode 100644 index 000000000000..f9d3128db76c --- /dev/null +++ b/packages/opencode/test/plugin/trigger.test.ts @@ -0,0 +1,131 @@ +import { afterAll, afterEach, describe, expect, test } from "bun:test" +import fs from "fs/promises" +import os from "os" +import path from "path" +import { pathToFileURL } from "url" + +const env = { + XDG_CONFIG_HOME: process.env.XDG_CONFIG_HOME, + XDG_DATA_HOME: process.env.XDG_DATA_HOME, + XDG_STATE_HOME: process.env.XDG_STATE_HOME, + XDG_CACHE_HOME: process.env.XDG_CACHE_HOME, + OPENCODE_TEST_HOME: process.env.OPENCODE_TEST_HOME, + OPENCODE_DISABLE_DEFAULT_PLUGINS: process.env.OPENCODE_DISABLE_DEFAULT_PLUGINS, +} + +const root = await fs.mkdtemp(path.join(os.tmpdir(), "opencode-plugin-trigger-xdg-")) + +process.env.XDG_CONFIG_HOME = path.join(root, "config") +process.env.XDG_DATA_HOME = path.join(root, "data") +process.env.XDG_STATE_HOME = path.join(root, "state") +process.env.XDG_CACHE_HOME = path.join(root, "cache") +process.env.OPENCODE_TEST_HOME = path.join(root, "home") +process.env.OPENCODE_DISABLE_DEFAULT_PLUGINS = "1" + +const { Plugin } = await import("../../src/plugin/index") +const { Instance } = await import("../../src/project/instance") + +afterEach(async () => { + await Instance.disposeAll() +}) + +afterAll(async () => { + for (const [key, value] of Object.entries(env)) { + if (value === undefined) delete process.env[key] + else process.env[key] = value + } + await fs.rm(root, { recursive: true, force: true }) +}) + +async function project(source: string) { + const dir = await fs.mkdtemp(path.join(os.tmpdir(), "opencode-plugin-trigger-")) + const file = path.join(dir, "plugin.ts") + await Bun.write(file, source) + await Bun.write( + path.join(dir, "opencode.json"), + JSON.stringify( + { + $schema: "https://opencode.ai/config.json", + plugin: [pathToFileURL(file).href], + }, + null, + 2, + ), + ) + + return { + path: dir, + async [Symbol.asyncDispose]() { + await fs.rm(dir, { recursive: true, force: true }) + }, + } +} + +describe("plugin.trigger", () => { + test("runs synchronous hooks without crashing", async () => { + await using tmp = await project( + [ + "export default async () => ({", + ' "experimental.chat.system.transform": (_input, output) => {', + ' output.system.unshift("sync")', + " },", + "})", + "", + ].join("\n"), + ) + + const out = await Instance.provide({ + directory: tmp.path, + fn: async () => { + const out = { system: [] as string[] } + await Plugin.trigger( + "experimental.chat.system.transform", + { + model: { + providerID: "anthropic", + modelID: "claude-sonnet-4-6", + } as any, + }, + out, + ) + return out + }, + }) + + expect(out.system).toEqual(["sync"]) + }) + + test("awaits asynchronous hooks", async () => { + await using tmp = await project( + [ + "export default async () => ({", + ' "experimental.chat.system.transform": async (_input, output) => {', + " await Bun.sleep(1)", + ' output.system.unshift("async")', + " },", + "})", + "", + ].join("\n"), + ) + + const out = await Instance.provide({ + directory: tmp.path, + fn: async () => { + const out = { system: [] as string[] } + await Plugin.trigger( + "experimental.chat.system.transform", + { + model: { + providerID: "anthropic", + modelID: "claude-sonnet-4-6", + } as any, + }, + out, + ) + return out + }, + }) + + expect(out.system).toEqual(["async"]) + }) +}) From 81f2c04986536a71ffd3a576350014f2e3395cd6 Mon Sep 17 00:00:00 2001 From: LukeParkerDev <10430890+Hona@users.noreply.github.com> Date: Sun, 29 Mar 2026 13:32:05 +1000 Subject: [PATCH 2/2] test: align plugin trigger regression with fixture patterns --- packages/opencode/test/plugin/trigger.test.ts | 66 +++++++------------ 1 file changed, 23 insertions(+), 43 deletions(-) diff --git a/packages/opencode/test/plugin/trigger.test.ts b/packages/opencode/test/plugin/trigger.test.ts index f9d3128db76c..d89d930c69ea 100644 --- a/packages/opencode/test/plugin/trigger.test.ts +++ b/packages/opencode/test/plugin/trigger.test.ts @@ -1,25 +1,9 @@ import { afterAll, afterEach, describe, expect, test } from "bun:test" -import fs from "fs/promises" -import os from "os" import path from "path" import { pathToFileURL } from "url" +import { tmpdir } from "../fixture/fixture" -const env = { - XDG_CONFIG_HOME: process.env.XDG_CONFIG_HOME, - XDG_DATA_HOME: process.env.XDG_DATA_HOME, - XDG_STATE_HOME: process.env.XDG_STATE_HOME, - XDG_CACHE_HOME: process.env.XDG_CACHE_HOME, - OPENCODE_TEST_HOME: process.env.OPENCODE_TEST_HOME, - OPENCODE_DISABLE_DEFAULT_PLUGINS: process.env.OPENCODE_DISABLE_DEFAULT_PLUGINS, -} - -const root = await fs.mkdtemp(path.join(os.tmpdir(), "opencode-plugin-trigger-xdg-")) - -process.env.XDG_CONFIG_HOME = path.join(root, "config") -process.env.XDG_DATA_HOME = path.join(root, "data") -process.env.XDG_STATE_HOME = path.join(root, "state") -process.env.XDG_CACHE_HOME = path.join(root, "cache") -process.env.OPENCODE_TEST_HOME = path.join(root, "home") +const disableDefault = process.env.OPENCODE_DISABLE_DEFAULT_PLUGINS process.env.OPENCODE_DISABLE_DEFAULT_PLUGINS = "1" const { Plugin } = await import("../../src/plugin/index") @@ -29,36 +13,32 @@ afterEach(async () => { await Instance.disposeAll() }) -afterAll(async () => { - for (const [key, value] of Object.entries(env)) { - if (value === undefined) delete process.env[key] - else process.env[key] = value +afterAll(() => { + if (disableDefault === undefined) { + delete process.env.OPENCODE_DISABLE_DEFAULT_PLUGINS + return } - await fs.rm(root, { recursive: true, force: true }) + process.env.OPENCODE_DISABLE_DEFAULT_PLUGINS = disableDefault }) async function project(source: string) { - const dir = await fs.mkdtemp(path.join(os.tmpdir(), "opencode-plugin-trigger-")) - const file = path.join(dir, "plugin.ts") - await Bun.write(file, source) - await Bun.write( - path.join(dir, "opencode.json"), - JSON.stringify( - { - $schema: "https://opencode.ai/config.json", - plugin: [pathToFileURL(file).href], - }, - null, - 2, - ), - ) - - return { - path: dir, - async [Symbol.asyncDispose]() { - await fs.rm(dir, { recursive: true, force: true }) + return tmpdir({ + init: async (dir) => { + const file = path.join(dir, "plugin.ts") + await Bun.write(file, source) + await Bun.write( + path.join(dir, "opencode.json"), + JSON.stringify( + { + $schema: "https://opencode.ai/config.json", + plugin: [pathToFileURL(file).href], + }, + null, + 2, + ), + ) }, - } + }) } describe("plugin.trigger", () => {