Buckets:
| import fs from "fs/promises" | |
| import path from "path" | |
| import { pathToFileURL } from "url" | |
| import { describe, expect } from "bun:test" | |
| import { Effect, Layer } from "effect" | |
| import { FSUtil } from "@opencode-ai/core/fs-util" | |
| import { PermissionV2 } from "@opencode-ai/core/permission" | |
| import { PluginBoot } from "@opencode-ai/core/plugin/boot" | |
| import { AbsolutePath } from "@opencode-ai/core/schema" | |
| import { SessionV2 } from "@opencode-ai/core/session" | |
| import { SkillV2 } from "@opencode-ai/core/skill" | |
| import { SkillTool } from "@opencode-ai/core/tool/skill" | |
| import { ToolRegistry } from "@opencode-ai/core/tool/registry" | |
| import { tmpdir } from "./fixture/tmpdir" | |
| import { it } from "./lib/effect" | |
| import { toolIdentity, executeTool, settleTool, toolDefinitions } from "./lib/tool" | |
| const sessionID = SessionV2.ID.make("ses_skill_tool_test") | |
| describe("SkillTool", () => { | |
| it.live("lists available skills, authorizes the selected name, and loads model-facing content", () => | |
| Effect.acquireRelease( | |
| Effect.promise(() => tmpdir()), | |
| (tmp) => Effect.promise(() => tmp[Symbol.asyncDispose]()), | |
| ).pipe( | |
| Effect.flatMap((tmp) => | |
| Effect.gen(function* () { | |
| const directory = path.join(tmp.path, "effect") | |
| const location = path.join(directory, "SKILL.md") | |
| const reference = path.join(directory, "reference.md") | |
| yield* Effect.promise(() => fs.mkdir(directory, { recursive: true })) | |
| yield* Effect.promise(() => | |
| Promise.all([fs.writeFile(location, "unused"), fs.writeFile(reference, "reference")]), | |
| ) | |
| const info: SkillV2.Info = { | |
| name: "effect", | |
| description: "Use Effect", | |
| location: AbsolutePath.make(location), | |
| content: "# Effect\n\nGuidance", | |
| } | |
| let current = [info] | |
| const assertions: PermissionV2.AssertInput[] = [] | |
| let deny = false | |
| let bootWaited = false | |
| const boot = Layer.succeed( | |
| PluginBoot.Service, | |
| PluginBoot.Service.of({ | |
| wait: () => | |
| Effect.sync(() => { | |
| bootWaited = true | |
| }), | |
| }), | |
| ) | |
| const permission = Layer.succeed( | |
| PermissionV2.Service, | |
| PermissionV2.Service.of({ | |
| assert: (input) => | |
| Effect.sync(() => assertions.push(input)).pipe( | |
| Effect.andThen(deny ? Effect.fail(new PermissionV2.DeniedError({ rules: [] })) : Effect.void), | |
| ), | |
| ask: () => Effect.die("unused"), | |
| reply: () => Effect.die("unused"), | |
| get: () => Effect.die("unused"), | |
| forSession: () => Effect.die("unused"), | |
| list: () => Effect.die("unused"), | |
| }), | |
| ) | |
| const skills = Layer.succeed( | |
| SkillV2.Service, | |
| SkillV2.Service.of({ | |
| transform: () => Effect.die("unused"), | |
| sources: () => Effect.die("unused"), | |
| list: () => Effect.succeed(current), | |
| }), | |
| ) | |
| const registry = ToolRegistry.defaultLayer.pipe(Layer.provide(permission)) | |
| const tool = SkillTool.layer.pipe( | |
| Layer.provide(registry), | |
| Layer.provide(permission), | |
| Layer.provide(FSUtil.defaultLayer), | |
| Layer.provide(boot), | |
| Layer.provide(skills), | |
| ) | |
| const layer = Layer.mergeAll(permission, skills, registry, boot, tool) | |
| return yield* Effect.gen(function* () { | |
| const registry = yield* ToolRegistry.Service | |
| expect(bootWaited).toBe(true) | |
| expect((yield* toolDefinitions(registry))[0]).toMatchObject({ | |
| name: "skill", | |
| description: SkillTool.description, | |
| }) | |
| expect( | |
| yield* executeTool(registry, { | |
| sessionID, | |
| ...toolIdentity, | |
| call: { type: "tool-call", id: "call-skill", name: "skill", input: { name: "effect" } }, | |
| }), | |
| ).toEqual({ | |
| type: "text", | |
| value: SkillTool.toModelOutput(info, [reference]), | |
| }) | |
| expect(SkillTool.toModelOutput(info, [reference])).toContain( | |
| `Base directory for this skill: ${pathToFileURL(directory).href}`, | |
| ) | |
| expect( | |
| yield* settleTool(registry, { | |
| sessionID, | |
| ...toolIdentity, | |
| call: { type: "tool-call", id: "call-skill-overflow", name: "skill", input: { name: "effect" } }, | |
| }), | |
| ).toMatchObject({ | |
| result: { type: "text", value: SkillTool.toModelOutput(info, [reference]) }, | |
| output: { structured: { name: "effect" } }, | |
| }) | |
| expect(assertions).toMatchObject([ | |
| { sessionID, action: "skill", resources: ["effect"], save: ["effect"] }, | |
| { sessionID, action: "skill", resources: ["effect"], save: ["effect"] }, | |
| ]) | |
| expect( | |
| yield* executeTool(registry, { | |
| sessionID, | |
| ...toolIdentity, | |
| call: { type: "tool-call", id: "call-missing-skill", name: "skill", input: { name: "missing" } }, | |
| }), | |
| ).toEqual({ type: "error", value: "Unable to load skill missing" }) | |
| deny = true | |
| expect( | |
| yield* executeTool(registry, { | |
| sessionID, | |
| ...toolIdentity, | |
| call: { type: "tool-call", id: "call-denied-skill", name: "skill", input: { name: "effect" } }, | |
| }), | |
| ).toEqual({ type: "error", value: "Unable to load skill effect" }) | |
| deny = false | |
| const flat = new SkillV2.Info({ | |
| name: "public", | |
| description: "Public guidance", | |
| location: AbsolutePath.make(path.join(tmp.path, "public.md")), | |
| content: "Public", | |
| }) | |
| yield* Effect.promise(() => | |
| Promise.all([ | |
| fs.writeFile(flat.location, "public"), | |
| fs.writeFile(path.join(tmp.path, "secret.md"), "secret"), | |
| ]), | |
| ) | |
| current = [flat] | |
| expect( | |
| yield* executeTool(registry, { | |
| sessionID, | |
| ...toolIdentity, | |
| call: { type: "tool-call", id: "call-flat-skill", name: "skill", input: { name: "public" } }, | |
| }), | |
| ).toEqual({ type: "text", value: SkillTool.toModelOutput(flat, []) }) | |
| }).pipe(Effect.provide(layer)) | |
| }), | |
| ), | |
| ), | |
| ) | |
| }) | |
Xet Storage Details
- Size:
- 6.72 kB
- Xet hash:
- 21428dfc2669b6bfc8cc008fcce06b1ed8d51afc598f4b1f29496dbd48f1be50
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.