test(skill): migrate skill tests to effect fixtures (#29046)

This commit is contained in:
Kit Langton
2026-05-24 20:12:48 -04:00
committed by GitHub
parent f00a681fc5
commit ea27e2457e
2 changed files with 285 additions and 312 deletions
+3
View File
@@ -16,6 +16,7 @@ import * as Log from "@opencode-ai/core/util/log"
import { Discovery } from "./discovery" import { Discovery } from "./discovery"
import CUSTOMIZE_OPENCODE_SKILL_BODY from "./prompt/customize-opencode.md" with { type: "text" } import CUSTOMIZE_OPENCODE_SKILL_BODY from "./prompt/customize-opencode.md" with { type: "text" }
import { isRecord } from "@/util/record" import { isRecord } from "@/util/record"
import { serviceUse } from "@opencode-ai/core/effect/service-use"
const log = Log.create({ service: "skill" }) const log = Log.create({ service: "skill" })
const CLAUDE_EXTERNAL_DIR = ".claude" const CLAUDE_EXTERNAL_DIR = ".claude"
@@ -243,6 +244,8 @@ const loadSkills = Effect.fnUntraced(function* (state: State, discovered: Discov
export class Service extends Context.Service<Service, Interface>()("@opencode/Skill") {} export class Service extends Context.Service<Service, Interface>()("@opencode/Skill") {}
export const use = serviceUse(Service)
export const layer = Layer.effect( export const layer = Layer.effect(
Service, Service,
Effect.gen(function* () { Effect.gen(function* () {
+282 -312
View File
@@ -8,14 +8,13 @@ import { Config } from "../../src/config/config"
import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner" import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner"
import { AppFileSystem } from "@opencode-ai/core/filesystem" import { AppFileSystem } from "@opencode-ai/core/filesystem"
import { Global } from "@opencode-ai/core/global" import { Global } from "@opencode-ai/core/global"
import { provideInstance, provideTmpdirInstance, tmpdir } from "../fixture/fixture" import { TestInstance } from "../fixture/fixture"
import { testEffect } from "../lib/effect" import { testEffect } from "../lib/effect"
import path from "path" import path from "path"
import fs from "fs/promises"
const node = CrossSpawnSpawner.defaultLayer const node = CrossSpawnSpawner.defaultLayer
const it = testEffect(Layer.mergeAll(Skill.defaultLayer, node)) const it = testEffect(Layer.mergeAll(Skill.defaultLayer, AppFileSystem.defaultLayer, node))
const itWithoutClaudeCodeSkills = testEffect( const itWithoutClaudeCodeSkills = testEffect(
Layer.mergeAll( Layer.mergeAll(
Skill.layer.pipe( Skill.layer.pipe(
@@ -26,6 +25,7 @@ const itWithoutClaudeCodeSkills = testEffect(
Layer.provide(Global.layer), Layer.provide(Global.layer),
Layer.provide(RuntimeFlags.layer({ disableClaudeCodeSkills: true })), Layer.provide(RuntimeFlags.layer({ disableClaudeCodeSkills: true })),
), ),
AppFileSystem.defaultLayer,
node, node,
), ),
) )
@@ -39,15 +39,18 @@ const itWithoutExternalSkills = testEffect(
Layer.provide(Global.layer), Layer.provide(Global.layer),
Layer.provide(RuntimeFlags.layer({ disableExternalSkills: true })), Layer.provide(RuntimeFlags.layer({ disableExternalSkills: true })),
), ),
AppFileSystem.defaultLayer,
node, node,
), ),
) )
async function createGlobalSkill(homeDir: string) { const writeSkill = (dir: string, parts: string[], content: string) =>
const skillDir = path.join(homeDir, ".claude", "skills", "global-test-skill") AppFileSystem.use.writeWithDirs(path.join(dir, ...parts, "SKILL.md"), content)
await fs.mkdir(skillDir, { recursive: true })
await Bun.write( const createGlobalSkill = (homeDir: string) =>
path.join(skillDir, "SKILL.md"), writeSkill(
homeDir,
[".claude", "skills", "global-test-skill"],
`--- `---
name: global-test-skill name: global-test-skill
description: A global skill from ~/.claude/skills for testing. description: A global skill from ~/.claude/skills for testing.
@@ -58,7 +61,6 @@ description: A global skill from ~/.claude/skills for testing.
This skill is loaded from the global home directory. This skill is loaded from the global home directory.
`, `,
) )
}
const withHome = <A, E, R>(home: string, self: Effect.Effect<A, E, R>) => const withHome = <A, E, R>(home: string, self: Effect.Effect<A, E, R>) =>
Effect.acquireUseRelease( Effect.acquireUseRelease(
@@ -75,14 +77,14 @@ const withHome = <A, E, R>(home: string, self: Effect.Effect<A, E, R>) =>
) )
describe("skill", () => { describe("skill", () => {
it.live("discovers skills from .opencode/skill/ directory", () => it.instance(
provideTmpdirInstance( "discovers skills from .opencode/skill/ directory",
(dir) => Effect.gen(function* () {
Effect.gen(function* () { const test = yield* TestInstance
yield* Effect.promise(() => yield* writeSkill(
Bun.write( test.directory,
path.join(dir, ".opencode", "skill", "test-skill", "SKILL.md"), [".opencode", "skill", "test-skill"],
`--- `---
name: test-skill name: test-skill
description: A test skill for verification. description: A test skill for verification.
--- ---
@@ -91,118 +93,111 @@ description: A test skill for verification.
Instructions here. Instructions here.
`, `,
), )
)
const skill = yield* Skill.Service const list = (yield* Skill.use.all()).filter((s) => s.location !== "<built-in>")
const list = (yield* skill.all()).filter((s) => s.location !== "<built-in>") expect(list.length).toBe(1)
expect(list.length).toBe(1) const item = list.find((x) => x.name === "test-skill")
const item = list.find((x) => x.name === "test-skill") expect(item).toBeDefined()
expect(item).toBeDefined() expect(item!.description).toBe("A test skill for verification.")
expect(item!.description).toBe("A test skill for verification.") expect(item!.location).toContain(path.join("skill", "test-skill", "SKILL.md"))
expect(item!.location).toContain(path.join("skill", "test-skill", "SKILL.md")) }),
}), { git: true },
{ git: true },
),
) )
it.live("returns skill directories from Skill.dirs", () => it.instance(
provideTmpdirInstance( "returns skill directories from Skill.dirs",
(dir) => Effect.gen(function* () {
withHome( const test = yield* TestInstance
dir, yield* withHome(
Effect.gen(function* () { test.directory,
yield* Effect.promise(() => Effect.gen(function* () {
Bun.write( yield* writeSkill(
path.join(dir, ".opencode", "skill", "dir-skill", "SKILL.md"), test.directory,
`--- [".opencode", "skill", "dir-skill"],
`---
name: dir-skill name: dir-skill
description: Skill for dirs test. description: Skill for dirs test.
--- ---
# Dir Skill # Dir Skill
`, `,
), )
)
const skill = yield* Skill.Service const dirs = yield* Skill.use.dirs()
const dirs = yield* skill.dirs() expect(dirs).toContain(path.join(test.directory, ".opencode", "skill", "dir-skill"))
expect(dirs).toContain(path.join(dir, ".opencode", "skill", "dir-skill")) expect(dirs.length).toBe(1)
expect(dirs.length).toBe(1) }),
}), )
), }),
{ git: true }, { git: true },
),
) )
it.live("discovers multiple skills from .opencode/skill/ directory", () => it.instance(
provideTmpdirInstance( "discovers multiple skills from .opencode/skill/ directory",
(dir) => Effect.gen(function* () {
Effect.gen(function* () { const test = yield* TestInstance
yield* Effect.promise(() => yield* Effect.all(
Promise.all([ [
Bun.write( writeSkill(
path.join(dir, ".opencode", "skill", "skill-one", "SKILL.md"), test.directory,
`--- [".opencode", "skill", "skill-one"],
`---
name: skill-one name: skill-one
description: First test skill. description: First test skill.
--- ---
# Skill One # Skill One
`, `,
), ),
Bun.write( writeSkill(
path.join(dir, ".opencode", "skill", "skill-two", "SKILL.md"), test.directory,
`--- [".opencode", "skill", "skill-two"],
`---
name: skill-two name: skill-two
description: Second test skill. description: Second test skill.
--- ---
# Skill Two # Skill Two
`, `,
), ),
]), ],
) )
const skill = yield* Skill.Service const list = (yield* Skill.use.all()).filter((s) => s.location !== "<built-in>")
const list = (yield* skill.all()).filter((s) => s.location !== "<built-in>") expect(list.length).toBe(2)
expect(list.length).toBe(2) expect(list.find((x) => x.name === "skill-one")).toBeDefined()
expect(list.find((x) => x.name === "skill-one")).toBeDefined() expect(list.find((x) => x.name === "skill-two")).toBeDefined()
expect(list.find((x) => x.name === "skill-two")).toBeDefined() }),
}), { git: true },
{ git: true },
),
) )
it.live("skips skills with missing frontmatter", () => it.instance(
provideTmpdirInstance( "skips skills with missing frontmatter",
(dir) => Effect.gen(function* () {
Effect.gen(function* () { const test = yield* TestInstance
yield* Effect.promise(() => yield* writeSkill(
Bun.write( test.directory,
path.join(dir, ".opencode", "skill", "no-frontmatter", "SKILL.md"), [".opencode", "skill", "no-frontmatter"],
`# No Frontmatter `# No Frontmatter
Just some content without YAML frontmatter. Just some content without YAML frontmatter.
`, `,
), )
)
const skill = yield* Skill.Service expect((yield* Skill.use.all()).filter((s) => s.location !== "<built-in>")).toEqual([])
expect((yield* skill.all()).filter((s) => s.location !== "<built-in>")).toEqual([]) }),
}), { git: true },
{ git: true },
),
) )
it.live("discovers skills without descriptions", () => it.instance(
provideTmpdirInstance( "discovers skills without descriptions",
(dir) => Effect.gen(function* () {
Effect.gen(function* () { const test = yield* TestInstance
yield* Effect.promise(() => yield* writeSkill(
Bun.write( test.directory,
path.join(dir, ".opencode", "skill", "manual-skill", "SKILL.md"), [".opencode", "skill", "manual-skill"],
`--- `---
name: manual-skill name: manual-skill
--- ---
@@ -210,98 +205,81 @@ name: manual-skill
Instructions here. Instructions here.
`, `,
), )
)
const skill = yield* Skill.Service const list = (yield* Skill.use.all()).filter((s) => s.location !== "<built-in>")
const list = (yield* skill.all()).filter((s) => s.location !== "<built-in>") expect(list.length).toBe(1)
expect(list.length).toBe(1) const item = list.find((x) => x.name === "manual-skill")
const item = list.find((x) => x.name === "manual-skill") expect(item).toBeDefined()
expect(item).toBeDefined() expect(item!.description).toBeUndefined()
expect(item!.description).toBeUndefined() expect(Skill.fmt(list, { verbose: false })).toBe("No skills are currently available.")
expect(Skill.fmt(list, { verbose: false })).toBe("No skills are currently available.") expect(Skill.fmt(list, { verbose: true })).toBe("No skills are currently available.")
expect(Skill.fmt(list, { verbose: true })).toBe("No skills are currently available.") }),
}), { git: true },
{ git: true },
),
) )
it.live("discovers skills from .claude/skills/ directory", () => it.instance(
provideTmpdirInstance( "discovers skills from .claude/skills/ directory",
(dir) => Effect.gen(function* () {
Effect.gen(function* () { const test = yield* TestInstance
yield* Effect.promise(() => yield* writeSkill(
Bun.write( test.directory,
path.join(dir, ".claude", "skills", "claude-skill", "SKILL.md"), [".claude", "skills", "claude-skill"],
`--- `---
name: claude-skill name: claude-skill
description: A skill in the .claude/skills directory. description: A skill in the .claude/skills directory.
--- ---
# Claude Skill # Claude Skill
`, `,
),
)
const skill = yield* Skill.Service
const list = (yield* skill.all()).filter((s) => s.location !== "<built-in>")
expect(list.length).toBe(1)
const item = list.find((x) => x.name === "claude-skill")
expect(item).toBeDefined()
expect(item!.location).toContain(path.join(".claude", "skills", "claude-skill", "SKILL.md"))
}),
{ git: true },
),
)
it.live("discovers global skills from ~/.claude/skills/ directory", () =>
Effect.gen(function* () {
const tmp = yield* Effect.acquireRelease(
Effect.promise(() => tmpdir({ git: true })),
(tmp) => Effect.promise(() => tmp[Symbol.asyncDispose]()),
) )
const list = (yield* Skill.use.all()).filter((s) => s.location !== "<built-in>")
expect(list.length).toBe(1)
const item = list.find((x) => x.name === "claude-skill")
expect(item).toBeDefined()
expect(item!.location).toContain(path.join(".claude", "skills", "claude-skill", "SKILL.md"))
}),
{ git: true },
)
it.instance(
"discovers global skills from ~/.claude/skills/ directory",
Effect.gen(function* () {
const test = yield* TestInstance
yield* withHome( yield* withHome(
tmp.path, test.directory,
Effect.gen(function* () { Effect.gen(function* () {
yield* Effect.promise(() => createGlobalSkill(tmp.path)) yield* createGlobalSkill(test.directory)
yield* Effect.gen(function* () { const list = (yield* Skill.use.all()).filter((s) => s.location !== "<built-in>")
const skill = yield* Skill.Service expect(list.length).toBe(1)
const list = (yield* skill.all()).filter((s) => s.location !== "<built-in>") expect(list[0].name).toBe("global-test-skill")
expect(list.length).toBe(1) expect(list[0].description).toBe("A global skill from ~/.claude/skills for testing.")
expect(list[0].name).toBe("global-test-skill") expect(list[0].location).toContain(path.join(".claude", "skills", "global-test-skill", "SKILL.md"))
expect(list[0].description).toBe("A global skill from ~/.claude/skills for testing.")
expect(list[0].location).toContain(path.join(".claude", "skills", "global-test-skill", "SKILL.md"))
}).pipe(provideInstance(tmp.path))
}), }),
) )
}), }),
{ git: true },
) )
it.live("returns empty array when no skills exist", () => it.instance(
provideTmpdirInstance( "returns empty array when no skills exist",
() => Effect.gen(function* () {
Effect.gen(function* () { expect((yield* Skill.use.all()).filter((s) => s.location !== "<built-in>")).toEqual([])
const skill = yield* Skill.Service }),
expect((yield* skill.all()).filter((s) => s.location !== "<built-in>")).toEqual([]) { git: true },
}),
{ git: true },
),
) )
it.live("fails with typed error when requiring a missing skill", () => it.instance(
provideTmpdirInstance( "fails with typed error when requiring a missing skill",
() => Effect.gen(function* () {
Effect.gen(function* () { const error = yield* Effect.flip(Skill.use.require("missing-skill"))
const skill = yield* Skill.Service expect(error).toBeInstanceOf(Skill.NotFoundError)
const error = yield* Effect.flip(skill.require("missing-skill")) expect(error._tag).toBe("Skill.NotFoundError")
expect(error).toBeInstanceOf(Skill.NotFoundError) expect(error.name).toBe("missing-skill")
expect(error._tag).toBe("Skill.NotFoundError") expect(error.message).toContain('Skill "missing-skill" not found.')
expect(error.name).toBe("missing-skill") }),
expect(error.message).toContain('Skill "missing-skill" not found.') { git: true },
}),
{ git: true },
),
) )
it.effect("exposes tagged expected skill failure classes", () => it.effect("exposes tagged expected skill failure classes", () =>
@@ -320,50 +298,42 @@ description: A skill in the .claude/skills directory.
}), }),
) )
it.live("discovers skills from .agents/skills/ directory", () => it.instance(
provideTmpdirInstance( "discovers skills from .agents/skills/ directory",
(dir) => Effect.gen(function* () {
Effect.gen(function* () { const test = yield* TestInstance
yield* Effect.promise(() => yield* writeSkill(
Bun.write( test.directory,
path.join(dir, ".agents", "skills", "agent-skill", "SKILL.md"), [".agents", "skills", "agent-skill"],
`--- `---
name: agent-skill name: agent-skill
description: A skill in the .agents/skills directory. description: A skill in the .agents/skills directory.
--- ---
# Agent Skill # Agent Skill
`, `,
),
)
const skill = yield* Skill.Service
const list = (yield* skill.all()).filter((s) => s.location !== "<built-in>")
expect(list.length).toBe(1)
const item = list.find((x) => x.name === "agent-skill")
expect(item).toBeDefined()
expect(item!.location).toContain(path.join(".agents", "skills", "agent-skill", "SKILL.md"))
}),
{ git: true },
),
)
it.live("discovers global skills from ~/.agents/skills/ directory", () =>
Effect.gen(function* () {
const tmp = yield* Effect.acquireRelease(
Effect.promise(() => tmpdir({ git: true })),
(tmp) => Effect.promise(() => tmp[Symbol.asyncDispose]()),
) )
const list = (yield* Skill.use.all()).filter((s) => s.location !== "<built-in>")
expect(list.length).toBe(1)
const item = list.find((x) => x.name === "agent-skill")
expect(item).toBeDefined()
expect(item!.location).toContain(path.join(".agents", "skills", "agent-skill", "SKILL.md"))
}),
{ git: true },
)
it.instance(
"discovers global skills from ~/.agents/skills/ directory",
Effect.gen(function* () {
const test = yield* TestInstance
yield* withHome( yield* withHome(
tmp.path, test.directory,
Effect.gen(function* () { Effect.gen(function* () {
const skillDir = path.join(tmp.path, ".agents", "skills", "global-agent-skill") yield* writeSkill(
yield* Effect.promise(() => fs.mkdir(skillDir, { recursive: true })) test.directory,
yield* Effect.promise(() => [".agents", "skills", "global-agent-skill"],
Bun.write( `---
path.join(skillDir, "SKILL.md"),
`---
name: global-agent-skill name: global-agent-skill
description: A global skill from ~/.agents/skills for testing. description: A global skill from ~/.agents/skills for testing.
--- ---
@@ -372,198 +342,198 @@ description: A global skill from ~/.agents/skills for testing.
This skill is loaded from the global home directory. This skill is loaded from the global home directory.
`, `,
),
) )
yield* Effect.gen(function* () { const list = (yield* Skill.use.all()).filter((s) => s.location !== "<built-in>")
const skill = yield* Skill.Service expect(list.length).toBe(1)
const list = (yield* skill.all()).filter((s) => s.location !== "<built-in>") expect(list[0].name).toBe("global-agent-skill")
expect(list.length).toBe(1) expect(list[0].description).toBe("A global skill from ~/.agents/skills for testing.")
expect(list[0].name).toBe("global-agent-skill") expect(list[0].location).toContain(path.join(".agents", "skills", "global-agent-skill", "SKILL.md"))
expect(list[0].description).toBe("A global skill from ~/.agents/skills for testing.")
expect(list[0].location).toContain(path.join(".agents", "skills", "global-agent-skill", "SKILL.md"))
}).pipe(provideInstance(tmp.path))
}), }),
) )
}), }),
{ git: true },
) )
it.live("discovers skills from both .claude/skills/ and .agents/skills/", () => it.instance(
provideTmpdirInstance( "discovers skills from both .claude/skills/ and .agents/skills/",
(dir) => Effect.gen(function* () {
Effect.gen(function* () { const test = yield* TestInstance
yield* Effect.promise(() => yield* Effect.all(
Promise.all([ [
Bun.write( writeSkill(
path.join(dir, ".claude", "skills", "claude-skill", "SKILL.md"), test.directory,
`--- [".claude", "skills", "claude-skill"],
`---
name: claude-skill name: claude-skill
description: A skill in the .claude/skills directory. description: A skill in the .claude/skills directory.
--- ---
# Claude Skill # Claude Skill
`, `,
), ),
Bun.write( writeSkill(
path.join(dir, ".agents", "skills", "agent-skill", "SKILL.md"), test.directory,
`--- [".agents", "skills", "agent-skill"],
`---
name: agent-skill name: agent-skill
description: A skill in the .agents/skills directory. description: A skill in the .agents/skills directory.
--- ---
# Agent Skill # Agent Skill
`, `,
), ),
]), ],
) )
const skill = yield* Skill.Service const list = (yield* Skill.use.all()).filter((s) => s.location !== "<built-in>")
const list = (yield* skill.all()).filter((s) => s.location !== "<built-in>") expect(list.length).toBe(2)
expect(list.length).toBe(2) expect(list.find((x) => x.name === "claude-skill")).toBeDefined()
expect(list.find((x) => x.name === "claude-skill")).toBeDefined() expect(list.find((x) => x.name === "agent-skill")).toBeDefined()
expect(list.find((x) => x.name === "agent-skill")).toBeDefined() }),
}), { git: true },
{ git: true },
),
) )
itWithoutClaudeCodeSkills.live("skips Claude Code skills when disabled", () => itWithoutClaudeCodeSkills.instance(
provideTmpdirInstance( "skips Claude Code skills when disabled",
(dir) => Effect.gen(function* () {
Effect.gen(function* () { const test = yield* TestInstance
yield* Effect.promise(() => yield* Effect.all(
Promise.all([ [
Bun.write( writeSkill(
path.join(dir, ".claude", "skills", "claude-skill", "SKILL.md"), test.directory,
`--- [".claude", "skills", "claude-skill"],
`---
name: claude-skill name: claude-skill
description: A skill in the .claude/skills directory. description: A skill in the .claude/skills directory.
--- ---
# Claude Skill # Claude Skill
`, `,
), ),
Bun.write( writeSkill(
path.join(dir, ".agents", "skills", "agent-skill", "SKILL.md"), test.directory,
`--- [".agents", "skills", "agent-skill"],
`---
name: agent-skill name: agent-skill
description: A skill in the .agents/skills directory. description: A skill in the .agents/skills directory.
--- ---
# Agent Skill # Agent Skill
`, `,
), ),
]), ],
) )
const skill = yield* Skill.Service const list = (yield* Skill.use.all()).filter((s) => s.location !== "<built-in>")
const list = (yield* skill.all()).filter((s) => s.location !== "<built-in>") expect(list.map((s) => s.name)).toEqual(["agent-skill"])
expect(list.map((s) => s.name)).toEqual(["agent-skill"]) }),
}), { git: true },
{ git: true },
),
) )
itWithoutExternalSkills.live("skips external skill directories when disabled", () => itWithoutExternalSkills.instance(
provideTmpdirInstance( "skips external skill directories when disabled",
(dir) => Effect.gen(function* () {
Effect.gen(function* () { const test = yield* TestInstance
yield* Effect.promise(() => yield* Effect.all(
Promise.all([ [
Bun.write( writeSkill(
path.join(dir, ".claude", "skills", "claude-skill", "SKILL.md"), test.directory,
`--- [".claude", "skills", "claude-skill"],
`---
name: claude-skill name: claude-skill
description: A skill in the .claude/skills directory. description: A skill in the .claude/skills directory.
--- ---
# Claude Skill # Claude Skill
`, `,
), ),
Bun.write( writeSkill(
path.join(dir, ".agents", "skills", "agent-skill", "SKILL.md"), test.directory,
`--- [".agents", "skills", "agent-skill"],
`---
name: agent-skill name: agent-skill
description: A skill in the .agents/skills directory. description: A skill in the .agents/skills directory.
--- ---
# Agent Skill # Agent Skill
`, `,
), ),
Bun.write( writeSkill(
path.join(dir, ".opencode", "skill", "opencode-skill", "SKILL.md"), test.directory,
`--- [".opencode", "skill", "opencode-skill"],
`---
name: opencode-skill name: opencode-skill
description: A skill in the .opencode/skill directory. description: A skill in the .opencode/skill directory.
--- ---
# OpenCode Skill # OpenCode Skill
`, `,
), ),
]), ],
) )
const skill = yield* Skill.Service const list = (yield* Skill.use.all()).filter((s) => s.location !== "<built-in>")
const list = (yield* skill.all()).filter((s) => s.location !== "<built-in>") expect(list.map((s) => s.name)).toEqual(["opencode-skill"])
expect(list.map((s) => s.name)).toEqual(["opencode-skill"]) }),
}), { git: true },
{ git: true },
),
) )
it.live("properly resolves directories that skills live in", () => it.instance(
provideTmpdirInstance( "properly resolves directories that skills live in",
(dir) => Effect.gen(function* () {
Effect.gen(function* () { const test = yield* TestInstance
yield* Effect.promise(() => yield* Effect.all(
Promise.all([ [
Bun.write( writeSkill(
path.join(dir, ".claude", "skills", "claude-skill", "SKILL.md"), test.directory,
`--- [".claude", "skills", "claude-skill"],
`---
name: claude-skill name: claude-skill
description: A skill in the .claude/skills directory. description: A skill in the .claude/skills directory.
--- ---
# Claude Skill # Claude Skill
`, `,
), ),
Bun.write( writeSkill(
path.join(dir, ".agents", "skills", "agent-skill", "SKILL.md"), test.directory,
`--- [".agents", "skills", "agent-skill"],
`---
name: agent-skill name: agent-skill
description: A skill in the .agents/skills directory. description: A skill in the .agents/skills directory.
--- ---
# Agent Skill # Agent Skill
`, `,
), ),
Bun.write( writeSkill(
path.join(dir, ".opencode", "skill", "agent-skill", "SKILL.md"), test.directory,
`--- [".opencode", "skill", "agent-skill"],
`---
name: opencode-skill name: opencode-skill
description: A skill in the .opencode/skill directory. description: A skill in the .opencode/skill directory.
--- ---
# OpenCode Skill # OpenCode Skill
`, `,
), ),
Bun.write( writeSkill(
path.join(dir, ".opencode", "skills", "agent-skill", "SKILL.md"), test.directory,
`--- [".opencode", "skills", "agent-skill"],
`---
name: opencode-skill name: opencode-skill
description: A skill in the .opencode/skills directory. description: A skill in the .opencode/skills directory.
--- ---
# OpenCode Skill # OpenCode Skill
`, `,
), ),
]), ],
) )
const skill = yield* Skill.Service expect((yield* Skill.use.dirs()).length).toBe(4)
expect((yield* skill.dirs()).length).toBe(4) }),
}), { git: true },
{ git: true },
),
) )
}) })