import { afterAll, afterEach, describe, expect, test } from "bun:test" import path from "path" import { pathToFileURL } from "url" import { tmpdir } from "../fixture/fixture" const disableDefault = process.env.KILO_DISABLE_DEFAULT_PLUGINS process.env.KILO_DISABLE_DEFAULT_PLUGINS = "1" const { Plugin } = await import("../../src/plugin/index") const { Instance } = await import("../../src/project/instance") afterEach(async () => { await Instance.disposeAll() }) afterAll(() => { if (disableDefault === undefined) { delete process.env.KILO_DISABLE_DEFAULT_PLUGINS return } process.env.KILO_DISABLE_DEFAULT_PLUGINS = disableDefault }) async function project(source: string) { 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", () => { 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"]) }) })