2
0

trigger.test.ts 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. import { afterAll, afterEach, describe, expect, test } from "bun:test"
  2. import path from "path"
  3. import { pathToFileURL } from "url"
  4. import { tmpdir } from "../fixture/fixture"
  5. const disableDefault = process.env.KILO_DISABLE_DEFAULT_PLUGINS
  6. process.env.KILO_DISABLE_DEFAULT_PLUGINS = "1"
  7. const { Plugin } = await import("../../src/plugin/index")
  8. const { Instance } = await import("../../src/project/instance")
  9. afterEach(async () => {
  10. await Instance.disposeAll()
  11. })
  12. afterAll(() => {
  13. if (disableDefault === undefined) {
  14. delete process.env.KILO_DISABLE_DEFAULT_PLUGINS
  15. return
  16. }
  17. process.env.KILO_DISABLE_DEFAULT_PLUGINS = disableDefault
  18. })
  19. async function project(source: string) {
  20. return tmpdir({
  21. init: async (dir) => {
  22. const file = path.join(dir, "plugin.ts")
  23. await Bun.write(file, source)
  24. await Bun.write(
  25. path.join(dir, "opencode.json"),
  26. JSON.stringify(
  27. {
  28. $schema: "https://opencode.ai/config.json",
  29. plugin: [pathToFileURL(file).href],
  30. },
  31. null,
  32. 2,
  33. ),
  34. )
  35. },
  36. })
  37. }
  38. describe("plugin.trigger", () => {
  39. test("runs synchronous hooks without crashing", async () => {
  40. await using tmp = await project(
  41. [
  42. "export default async () => ({",
  43. ' "experimental.chat.system.transform": (_input, output) => {',
  44. ' output.system.unshift("sync")',
  45. " },",
  46. "})",
  47. "",
  48. ].join("\n"),
  49. )
  50. const out = await Instance.provide({
  51. directory: tmp.path,
  52. fn: async () => {
  53. const out = { system: [] as string[] }
  54. await Plugin.trigger(
  55. "experimental.chat.system.transform",
  56. {
  57. model: {
  58. providerID: "anthropic",
  59. modelID: "claude-sonnet-4-6",
  60. } as any,
  61. },
  62. out,
  63. )
  64. return out
  65. },
  66. })
  67. expect(out.system).toEqual(["sync"])
  68. })
  69. test("awaits asynchronous hooks", async () => {
  70. await using tmp = await project(
  71. [
  72. "export default async () => ({",
  73. ' "experimental.chat.system.transform": async (_input, output) => {',
  74. " await Bun.sleep(1)",
  75. ' output.system.unshift("async")',
  76. " },",
  77. "})",
  78. "",
  79. ].join("\n"),
  80. )
  81. const out = await Instance.provide({
  82. directory: tmp.path,
  83. fn: async () => {
  84. const out = { system: [] as string[] }
  85. await Plugin.trigger(
  86. "experimental.chat.system.transform",
  87. {
  88. model: {
  89. providerID: "anthropic",
  90. modelID: "claude-sonnet-4-6",
  91. } as any,
  92. },
  93. out,
  94. )
  95. return out
  96. },
  97. })
  98. expect(out.system).toEqual(["async"])
  99. })
  100. })