skill.test.ts 3.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
  1. import path from "path"
  2. import { describe, expect } from "bun:test"
  3. import { Effect, Layer, Schema } from "effect"
  4. import { Config } from "@opencode-ai/core/config"
  5. import { ConfigSkillPlugin } from "@opencode-ai/core/config/plugin/skill"
  6. import { Global } from "@opencode-ai/core/global"
  7. import { Location } from "@opencode-ai/core/location"
  8. import { AbsolutePath } from "@opencode-ai/core/schema"
  9. import { SkillV2 } from "@opencode-ai/core/skill"
  10. import { location } from "../fixture/location"
  11. import { testEffect } from "../lib/effect"
  12. import { host } from "../plugin/host"
  13. const it = testEffect(Layer.empty)
  14. const decode = Schema.decodeUnknownSync(Config.Info)
  15. describe("ConfigSkillPlugin.Plugin", () => {
  16. it.effect("registers configured skill directories and URLs", () =>
  17. Effect.gen(function* () {
  18. const directory = AbsolutePath.make("/repo/packages/app")
  19. const sources: SkillV2.Source[] = []
  20. const transform = Effect.fnUntraced(function* (update: (draft: SkillV2.Draft) => void | Effect.Effect<void>) {
  21. const result = update({
  22. source: (source) => {
  23. sources.push(source)
  24. },
  25. list: () => sources,
  26. })
  27. if (Effect.isEffect(result)) yield* result
  28. const dispose = Effect.sync(() => {
  29. sources.length = 0
  30. })
  31. yield* Effect.addFinalizer(() => dispose)
  32. return { dispose }
  33. })
  34. yield* ConfigSkillPlugin.Plugin.effect(
  35. host({
  36. skill: { transform, reload: () => Effect.void },
  37. }),
  38. ).pipe(
  39. Effect.provideService(Global.Service, Global.Service.of({ ...Global.make(), home: "/home/test" })),
  40. Effect.provideService(Location.Service, Location.Service.of(location({ directory }))),
  41. Effect.provideService(
  42. Config.Service,
  43. Config.Service.of({
  44. entries: () =>
  45. Effect.succeed([
  46. new Config.Directory({ type: "directory", path: AbsolutePath.make("/repo/.opencode") }),
  47. new Config.Document({
  48. type: "document",
  49. info: decode({
  50. skills: ["./skills", "~/shared-skills", "/opt/skills", "https://example.test/skills/"],
  51. }),
  52. }),
  53. ]),
  54. }),
  55. ),
  56. )
  57. expect(sources).toEqual([
  58. SkillV2.DirectorySource.make({
  59. type: "directory",
  60. path: AbsolutePath.make(path.join("/repo/.opencode", "skill")),
  61. }),
  62. SkillV2.DirectorySource.make({
  63. type: "directory",
  64. path: AbsolutePath.make(path.join("/repo/.opencode", "skills")),
  65. }),
  66. SkillV2.DirectorySource.make({ type: "directory", path: AbsolutePath.make(path.join(directory, "skills")) }),
  67. SkillV2.DirectorySource.make({
  68. type: "directory",
  69. path: AbsolutePath.make(path.join("/home/test", "shared-skills")),
  70. }),
  71. SkillV2.DirectorySource.make({ type: "directory", path: AbsolutePath.make("/opt/skills") }),
  72. SkillV2.UrlSource.make({ type: "url", url: "https://example.test/skills/" }),
  73. ])
  74. }),
  75. )
  76. })