|
|
@@ -0,0 +1,112 @@
|
|
|
+import { describe, expect, test } from "bun:test"
|
|
|
+import path from "path"
|
|
|
+import { pathToFileURL } from "url"
|
|
|
+import type { PermissionNext } from "../../src/permission/next"
|
|
|
+import type { Tool } from "../../src/tool/tool"
|
|
|
+import { Instance } from "../../src/project/instance"
|
|
|
+import { SkillTool } from "../../src/tool/skill"
|
|
|
+import { tmpdir } from "../fixture/fixture"
|
|
|
+
|
|
|
+const baseCtx: Omit<Tool.Context, "ask"> = {
|
|
|
+ sessionID: "test",
|
|
|
+ messageID: "",
|
|
|
+ callID: "",
|
|
|
+ agent: "build",
|
|
|
+ abort: AbortSignal.any([]),
|
|
|
+ messages: [],
|
|
|
+ metadata: () => {},
|
|
|
+}
|
|
|
+
|
|
|
+describe("tool.skill", () => {
|
|
|
+ test("description lists skill location URL", async () => {
|
|
|
+ await using tmp = await tmpdir({
|
|
|
+ git: true,
|
|
|
+ init: async (dir) => {
|
|
|
+ const skillDir = path.join(dir, ".opencode", "skill", "tool-skill")
|
|
|
+ await Bun.write(
|
|
|
+ path.join(skillDir, "SKILL.md"),
|
|
|
+ `---
|
|
|
+name: tool-skill
|
|
|
+description: Skill for tool tests.
|
|
|
+---
|
|
|
+
|
|
|
+# Tool Skill
|
|
|
+`,
|
|
|
+ )
|
|
|
+ },
|
|
|
+ })
|
|
|
+
|
|
|
+ const home = process.env.OPENCODE_TEST_HOME
|
|
|
+ process.env.OPENCODE_TEST_HOME = tmp.path
|
|
|
+
|
|
|
+ try {
|
|
|
+ await Instance.provide({
|
|
|
+ directory: tmp.path,
|
|
|
+ fn: async () => {
|
|
|
+ const tool = await SkillTool.init()
|
|
|
+ const skillPath = path.join(tmp.path, ".opencode", "skill", "tool-skill", "SKILL.md")
|
|
|
+ expect(tool.description).toContain(`<location>${pathToFileURL(skillPath).href}</location>`)
|
|
|
+ },
|
|
|
+ })
|
|
|
+ } finally {
|
|
|
+ process.env.OPENCODE_TEST_HOME = home
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ test("execute returns skill content block with files", async () => {
|
|
|
+ await using tmp = await tmpdir({
|
|
|
+ git: true,
|
|
|
+ init: async (dir) => {
|
|
|
+ const skillDir = path.join(dir, ".opencode", "skill", "tool-skill")
|
|
|
+ await Bun.write(
|
|
|
+ path.join(skillDir, "SKILL.md"),
|
|
|
+ `---
|
|
|
+name: tool-skill
|
|
|
+description: Skill for tool tests.
|
|
|
+---
|
|
|
+
|
|
|
+# Tool Skill
|
|
|
+
|
|
|
+Use this skill.
|
|
|
+`,
|
|
|
+ )
|
|
|
+ await Bun.write(path.join(skillDir, "scripts", "demo.txt"), "demo")
|
|
|
+ },
|
|
|
+ })
|
|
|
+
|
|
|
+ const home = process.env.OPENCODE_TEST_HOME
|
|
|
+ process.env.OPENCODE_TEST_HOME = tmp.path
|
|
|
+
|
|
|
+ try {
|
|
|
+ await Instance.provide({
|
|
|
+ directory: tmp.path,
|
|
|
+ fn: async () => {
|
|
|
+ const tool = await SkillTool.init()
|
|
|
+ const requests: Array<Omit<PermissionNext.Request, "id" | "sessionID" | "tool">> = []
|
|
|
+ const ctx: Tool.Context = {
|
|
|
+ ...baseCtx,
|
|
|
+ ask: async (req) => {
|
|
|
+ requests.push(req)
|
|
|
+ },
|
|
|
+ }
|
|
|
+
|
|
|
+ const result = await tool.execute({ name: "tool-skill" }, ctx)
|
|
|
+ const dir = path.join(tmp.path, ".opencode", "skill", "tool-skill")
|
|
|
+ const file = path.resolve(dir, "scripts", "demo.txt")
|
|
|
+
|
|
|
+ expect(requests.length).toBe(1)
|
|
|
+ expect(requests[0].permission).toBe("skill")
|
|
|
+ expect(requests[0].patterns).toContain("tool-skill")
|
|
|
+ expect(requests[0].always).toContain("tool-skill")
|
|
|
+
|
|
|
+ expect(result.metadata.dir).toBe(dir)
|
|
|
+ expect(result.output).toContain(`<skill_content name="tool-skill">`)
|
|
|
+ expect(result.output).toContain(`Base directory for this skill: ${pathToFileURL(dir).href}`)
|
|
|
+ expect(result.output).toContain(`<file>${file}</file>`)
|
|
|
+ },
|
|
|
+ })
|
|
|
+ } finally {
|
|
|
+ process.env.OPENCODE_TEST_HOME = home
|
|
|
+ }
|
|
|
+ })
|
|
|
+})
|