refactor(skill): remove async facade exports (#22308)
This commit is contained in:
@@ -1,4 +1,6 @@
|
|||||||
import { EOL } from "os"
|
import { EOL } from "os"
|
||||||
|
import { Effect } from "effect"
|
||||||
|
import { AppRuntime } from "@/effect/app-runtime"
|
||||||
import { Skill } from "../../../skill"
|
import { Skill } from "../../../skill"
|
||||||
import { bootstrap } from "../../bootstrap"
|
import { bootstrap } from "../../bootstrap"
|
||||||
import { cmd } from "../cmd"
|
import { cmd } from "../cmd"
|
||||||
@@ -9,7 +11,12 @@ export const SkillCommand = cmd({
|
|||||||
builder: (yargs) => yargs,
|
builder: (yargs) => yargs,
|
||||||
async handler() {
|
async handler() {
|
||||||
await bootstrap(process.cwd(), async () => {
|
await bootstrap(process.cwd(), async () => {
|
||||||
const skills = await Skill.all()
|
const skills = await AppRuntime.runPromise(
|
||||||
|
Effect.gen(function* () {
|
||||||
|
const skill = yield* Skill.Service
|
||||||
|
return yield* skill.all()
|
||||||
|
}),
|
||||||
|
)
|
||||||
process.stdout.write(JSON.stringify(skills, null, 2) + EOL)
|
process.stdout.write(JSON.stringify(skills, null, 2) + EOL)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import { ProviderRoutes } from "./provider"
|
|||||||
import { EventRoutes } from "./event"
|
import { EventRoutes } from "./event"
|
||||||
import { WorkspaceRouterMiddleware } from "./middleware"
|
import { WorkspaceRouterMiddleware } from "./middleware"
|
||||||
import { AppRuntime } from "@/effect/app-runtime"
|
import { AppRuntime } from "@/effect/app-runtime"
|
||||||
|
import { Effect } from "effect"
|
||||||
|
|
||||||
export const InstanceRoutes = (upgrade: UpgradeWebSocket): Hono =>
|
export const InstanceRoutes = (upgrade: UpgradeWebSocket): Hono =>
|
||||||
new Hono()
|
new Hono()
|
||||||
@@ -215,7 +216,12 @@ export const InstanceRoutes = (upgrade: UpgradeWebSocket): Hono =>
|
|||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
async (c) => {
|
async (c) => {
|
||||||
const skills = await Skill.all()
|
const skills = await AppRuntime.runPromise(
|
||||||
|
Effect.gen(function* () {
|
||||||
|
const skill = yield* Skill.Service
|
||||||
|
return yield* skill.all()
|
||||||
|
}),
|
||||||
|
)
|
||||||
return c.json(skills)
|
return c.json(skills)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import { NamedError } from "@opencode-ai/util/error"
|
|||||||
import type { Agent } from "@/agent/agent"
|
import type { Agent } from "@/agent/agent"
|
||||||
import { Bus } from "@/bus"
|
import { Bus } from "@/bus"
|
||||||
import { InstanceState } from "@/effect/instance-state"
|
import { InstanceState } from "@/effect/instance-state"
|
||||||
import { makeRuntime } from "@/effect/run-service"
|
|
||||||
import { Flag } from "@/flag/flag"
|
import { Flag } from "@/flag/flag"
|
||||||
import { Global } from "@/global"
|
import { Global } from "@/global"
|
||||||
import { Permission } from "@/permission"
|
import { Permission } from "@/permission"
|
||||||
@@ -262,22 +261,4 @@ export namespace Skill {
|
|||||||
.map((skill) => `- **${skill.name}**: ${skill.description}`),
|
.map((skill) => `- **${skill.name}**: ${skill.description}`),
|
||||||
].join("\n")
|
].join("\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
const { runPromise } = makeRuntime(Service, defaultLayer)
|
|
||||||
|
|
||||||
export async function get(name: string) {
|
|
||||||
return runPromise((skill) => skill.get(name))
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function all() {
|
|
||||||
return runPromise((skill) => skill.all())
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function dirs() {
|
|
||||||
return runPromise((skill) => skill.dirs())
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function available(agent?: Agent.Info) {
|
|
||||||
return runPromise((skill) => skill.available(agent))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,17 @@
|
|||||||
import { afterEach, test, expect } from "bun:test"
|
import { NodeChildProcessSpawner, NodeFileSystem, NodePath } from "@effect/platform-node"
|
||||||
|
import { describe, expect } from "bun:test"
|
||||||
|
import { Effect, Layer } from "effect"
|
||||||
import { Skill } from "../../src/skill"
|
import { Skill } from "../../src/skill"
|
||||||
import { Instance } from "../../src/project/instance"
|
import { provideInstance, provideTmpdirInstance, tmpdir } from "../fixture/fixture"
|
||||||
import { tmpdir } from "../fixture/fixture"
|
import { testEffect } from "../lib/effect"
|
||||||
import path from "path"
|
import path from "path"
|
||||||
import fs from "fs/promises"
|
import fs from "fs/promises"
|
||||||
|
|
||||||
afterEach(async () => {
|
const node = NodeChildProcessSpawner.layer.pipe(
|
||||||
await Instance.disposeAll()
|
Layer.provideMerge(Layer.mergeAll(NodeFileSystem.layer, NodePath.layer)),
|
||||||
})
|
)
|
||||||
|
|
||||||
|
const it = testEffect(Layer.mergeAll(Skill.defaultLayer, node))
|
||||||
|
|
||||||
async function createGlobalSkill(homeDir: string) {
|
async function createGlobalSkill(homeDir: string) {
|
||||||
const skillDir = path.join(homeDir, ".claude", "skills", "global-test-skill")
|
const skillDir = path.join(homeDir, ".claude", "skills", "global-test-skill")
|
||||||
@@ -26,14 +30,29 @@ This skill is loaded from the global home directory.
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
test("discovers skills from .opencode/skill/ directory", async () => {
|
const withHome = <A, E, R>(home: string, self: Effect.Effect<A, E, R>) =>
|
||||||
await using tmp = await tmpdir({
|
Effect.acquireUseRelease(
|
||||||
git: true,
|
Effect.sync(() => {
|
||||||
init: async (dir) => {
|
const prev = process.env.OPENCODE_TEST_HOME
|
||||||
const skillDir = path.join(dir, ".opencode", "skill", "test-skill")
|
process.env.OPENCODE_TEST_HOME = home
|
||||||
await Bun.write(
|
return prev
|
||||||
path.join(skillDir, "SKILL.md"),
|
}),
|
||||||
`---
|
() => self,
|
||||||
|
(prev) =>
|
||||||
|
Effect.sync(() => {
|
||||||
|
process.env.OPENCODE_TEST_HOME = prev
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
describe("skill", () => {
|
||||||
|
it.live("discovers skills from .opencode/skill/ directory", () =>
|
||||||
|
provideTmpdirInstance(
|
||||||
|
(dir) =>
|
||||||
|
Effect.gen(function* () {
|
||||||
|
yield* Effect.promise(() =>
|
||||||
|
Bun.write(
|
||||||
|
path.join(dir, ".opencode", "skill", "test-skill", "SKILL.md"),
|
||||||
|
`---
|
||||||
name: test-skill
|
name: test-skill
|
||||||
description: A test skill for verification.
|
description: A test skill for verification.
|
||||||
---
|
---
|
||||||
@@ -42,230 +61,217 @@ description: A test skill for verification.
|
|||||||
|
|
||||||
Instructions here.
|
Instructions here.
|
||||||
`,
|
`,
|
||||||
)
|
),
|
||||||
},
|
)
|
||||||
})
|
|
||||||
|
|
||||||
await Instance.provide({
|
const skill = yield* Skill.Service
|
||||||
directory: tmp.path,
|
const list = yield* skill.all()
|
||||||
fn: async () => {
|
expect(list.length).toBe(1)
|
||||||
const skills = await Skill.all()
|
const item = list.find((x) => x.name === "test-skill")
|
||||||
expect(skills.length).toBe(1)
|
expect(item).toBeDefined()
|
||||||
const testSkill = skills.find((s) => s.name === "test-skill")
|
expect(item!.description).toBe("A test skill for verification.")
|
||||||
expect(testSkill).toBeDefined()
|
expect(item!.location).toContain(path.join("skill", "test-skill", "SKILL.md"))
|
||||||
expect(testSkill!.description).toBe("A test skill for verification.")
|
}),
|
||||||
expect(testSkill!.location).toContain(path.join("skill", "test-skill", "SKILL.md"))
|
{ git: true },
|
||||||
},
|
),
|
||||||
})
|
)
|
||||||
})
|
|
||||||
|
|
||||||
test("returns skill directories from Skill.dirs", async () => {
|
it.live("returns skill directories from Skill.dirs", () =>
|
||||||
await using tmp = await tmpdir({
|
provideTmpdirInstance(
|
||||||
git: true,
|
(dir) =>
|
||||||
init: async (dir) => {
|
withHome(
|
||||||
const skillDir = path.join(dir, ".opencode", "skill", "dir-skill")
|
dir,
|
||||||
await Bun.write(
|
Effect.gen(function* () {
|
||||||
path.join(skillDir, "SKILL.md"),
|
yield* Effect.promise(() =>
|
||||||
`---
|
Bun.write(
|
||||||
|
path.join(dir, ".opencode", "skill", "dir-skill", "SKILL.md"),
|
||||||
|
`---
|
||||||
name: dir-skill
|
name: dir-skill
|
||||||
description: Skill for dirs test.
|
description: Skill for dirs test.
|
||||||
---
|
---
|
||||||
|
|
||||||
# Dir Skill
|
# Dir Skill
|
||||||
`,
|
`,
|
||||||
)
|
),
|
||||||
},
|
)
|
||||||
})
|
|
||||||
|
|
||||||
const home = process.env.OPENCODE_TEST_HOME
|
const skill = yield* Skill.Service
|
||||||
process.env.OPENCODE_TEST_HOME = tmp.path
|
const dirs = yield* skill.dirs()
|
||||||
|
expect(dirs).toContain(path.join(dir, ".opencode", "skill", "dir-skill"))
|
||||||
|
expect(dirs.length).toBe(1)
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
{ git: true },
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
try {
|
it.live("discovers multiple skills from .opencode/skill/ directory", () =>
|
||||||
await Instance.provide({
|
provideTmpdirInstance(
|
||||||
directory: tmp.path,
|
(dir) =>
|
||||||
fn: async () => {
|
Effect.gen(function* () {
|
||||||
const dirs = await Skill.dirs()
|
yield* Effect.promise(() =>
|
||||||
const skillDir = path.join(tmp.path, ".opencode", "skill", "dir-skill")
|
Promise.all([
|
||||||
expect(dirs).toContain(skillDir)
|
Bun.write(
|
||||||
expect(dirs.length).toBe(1)
|
path.join(dir, ".opencode", "skill", "skill-one", "SKILL.md"),
|
||||||
},
|
`---
|
||||||
})
|
|
||||||
} finally {
|
|
||||||
process.env.OPENCODE_TEST_HOME = home
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
test("discovers multiple skills from .opencode/skill/ directory", async () => {
|
|
||||||
await using tmp = await tmpdir({
|
|
||||||
git: true,
|
|
||||||
init: async (dir) => {
|
|
||||||
const skillDir1 = path.join(dir, ".opencode", "skill", "skill-one")
|
|
||||||
const skillDir2 = path.join(dir, ".opencode", "skill", "skill-two")
|
|
||||||
await Bun.write(
|
|
||||||
path.join(skillDir1, "SKILL.md"),
|
|
||||||
`---
|
|
||||||
name: skill-one
|
name: skill-one
|
||||||
description: First test skill.
|
description: First test skill.
|
||||||
---
|
---
|
||||||
|
|
||||||
# Skill One
|
# Skill One
|
||||||
`,
|
`,
|
||||||
)
|
),
|
||||||
await Bun.write(
|
Bun.write(
|
||||||
path.join(skillDir2, "SKILL.md"),
|
path.join(dir, ".opencode", "skill", "skill-two", "SKILL.md"),
|
||||||
`---
|
`---
|
||||||
name: skill-two
|
name: skill-two
|
||||||
description: Second test skill.
|
description: Second test skill.
|
||||||
---
|
---
|
||||||
|
|
||||||
# Skill Two
|
# Skill Two
|
||||||
`,
|
`,
|
||||||
)
|
),
|
||||||
},
|
]),
|
||||||
})
|
)
|
||||||
|
|
||||||
await Instance.provide({
|
const skill = yield* Skill.Service
|
||||||
directory: tmp.path,
|
const list = yield* skill.all()
|
||||||
fn: async () => {
|
expect(list.length).toBe(2)
|
||||||
const skills = await Skill.all()
|
expect(list.find((x) => x.name === "skill-one")).toBeDefined()
|
||||||
expect(skills.length).toBe(2)
|
expect(list.find((x) => x.name === "skill-two")).toBeDefined()
|
||||||
expect(skills.find((s) => s.name === "skill-one")).toBeDefined()
|
}),
|
||||||
expect(skills.find((s) => s.name === "skill-two")).toBeDefined()
|
{ git: true },
|
||||||
},
|
),
|
||||||
})
|
)
|
||||||
})
|
|
||||||
|
|
||||||
test("skips skills with missing frontmatter", async () => {
|
it.live("skips skills with missing frontmatter", () =>
|
||||||
await using tmp = await tmpdir({
|
provideTmpdirInstance(
|
||||||
git: true,
|
(dir) =>
|
||||||
init: async (dir) => {
|
Effect.gen(function* () {
|
||||||
const skillDir = path.join(dir, ".opencode", "skill", "no-frontmatter")
|
yield* Effect.promise(() =>
|
||||||
await Bun.write(
|
Bun.write(
|
||||||
path.join(skillDir, "SKILL.md"),
|
path.join(dir, ".opencode", "skill", "no-frontmatter", "SKILL.md"),
|
||||||
`# No Frontmatter
|
`# No Frontmatter
|
||||||
|
|
||||||
Just some content without YAML frontmatter.
|
Just some content without YAML frontmatter.
|
||||||
`,
|
`,
|
||||||
)
|
),
|
||||||
},
|
)
|
||||||
})
|
|
||||||
|
|
||||||
await Instance.provide({
|
const skill = yield* Skill.Service
|
||||||
directory: tmp.path,
|
expect(yield* skill.all()).toEqual([])
|
||||||
fn: async () => {
|
}),
|
||||||
const skills = await Skill.all()
|
{ git: true },
|
||||||
expect(skills).toEqual([])
|
),
|
||||||
},
|
)
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
test("discovers skills from .claude/skills/ directory", async () => {
|
it.live("discovers skills from .claude/skills/ directory", () =>
|
||||||
await using tmp = await tmpdir({
|
provideTmpdirInstance(
|
||||||
git: true,
|
(dir) =>
|
||||||
init: async (dir) => {
|
Effect.gen(function* () {
|
||||||
const skillDir = path.join(dir, ".claude", "skills", "claude-skill")
|
yield* Effect.promise(() =>
|
||||||
await Bun.write(
|
Bun.write(
|
||||||
path.join(skillDir, "SKILL.md"),
|
path.join(dir, ".claude", "skills", "claude-skill", "SKILL.md"),
|
||||||
`---
|
`---
|
||||||
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()
|
||||||
|
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]()),
|
||||||
)
|
)
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
await Instance.provide({
|
yield* withHome(
|
||||||
directory: tmp.path,
|
tmp.path,
|
||||||
fn: async () => {
|
Effect.gen(function* () {
|
||||||
const skills = await Skill.all()
|
yield* Effect.promise(() => createGlobalSkill(tmp.path))
|
||||||
expect(skills.length).toBe(1)
|
yield* Effect.gen(function* () {
|
||||||
const claudeSkill = skills.find((s) => s.name === "claude-skill")
|
const skill = yield* Skill.Service
|
||||||
expect(claudeSkill).toBeDefined()
|
const list = yield* skill.all()
|
||||||
expect(claudeSkill!.location).toContain(path.join(".claude", "skills", "claude-skill", "SKILL.md"))
|
expect(list.length).toBe(1)
|
||||||
},
|
expect(list[0].name).toBe("global-test-skill")
|
||||||
})
|
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))
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
test("discovers global skills from ~/.claude/skills/ directory", async () => {
|
it.live("returns empty array when no skills exist", () =>
|
||||||
await using tmp = await tmpdir({ git: true })
|
provideTmpdirInstance(
|
||||||
|
() =>
|
||||||
|
Effect.gen(function* () {
|
||||||
|
const skill = yield* Skill.Service
|
||||||
|
expect(yield* skill.all()).toEqual([])
|
||||||
|
}),
|
||||||
|
{ git: true },
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
const originalHome = process.env.OPENCODE_TEST_HOME
|
it.live("discovers skills from .agents/skills/ directory", () =>
|
||||||
process.env.OPENCODE_TEST_HOME = tmp.path
|
provideTmpdirInstance(
|
||||||
|
(dir) =>
|
||||||
try {
|
Effect.gen(function* () {
|
||||||
await createGlobalSkill(tmp.path)
|
yield* Effect.promise(() =>
|
||||||
await Instance.provide({
|
Bun.write(
|
||||||
directory: tmp.path,
|
path.join(dir, ".agents", "skills", "agent-skill", "SKILL.md"),
|
||||||
fn: async () => {
|
`---
|
||||||
const skills = await Skill.all()
|
|
||||||
expect(skills.length).toBe(1)
|
|
||||||
expect(skills[0].name).toBe("global-test-skill")
|
|
||||||
expect(skills[0].description).toBe("A global skill from ~/.claude/skills for testing.")
|
|
||||||
expect(skills[0].location).toContain(path.join(".claude", "skills", "global-test-skill", "SKILL.md"))
|
|
||||||
},
|
|
||||||
})
|
|
||||||
} finally {
|
|
||||||
process.env.OPENCODE_TEST_HOME = originalHome
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
test("returns empty array when no skills exist", async () => {
|
|
||||||
await using tmp = await tmpdir({ git: true })
|
|
||||||
|
|
||||||
await Instance.provide({
|
|
||||||
directory: tmp.path,
|
|
||||||
fn: async () => {
|
|
||||||
const skills = await Skill.all()
|
|
||||||
expect(skills).toEqual([])
|
|
||||||
},
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
test("discovers skills from .agents/skills/ directory", async () => {
|
|
||||||
await using tmp = await tmpdir({
|
|
||||||
git: true,
|
|
||||||
init: async (dir) => {
|
|
||||||
const skillDir = path.join(dir, ".agents", "skills", "agent-skill")
|
|
||||||
await Bun.write(
|
|
||||||
path.join(skillDir, "SKILL.md"),
|
|
||||||
`---
|
|
||||||
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()
|
||||||
|
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]()),
|
||||||
)
|
)
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
await Instance.provide({
|
yield* withHome(
|
||||||
directory: tmp.path,
|
tmp.path,
|
||||||
fn: async () => {
|
Effect.gen(function* () {
|
||||||
const skills = await Skill.all()
|
const skillDir = path.join(tmp.path, ".agents", "skills", "global-agent-skill")
|
||||||
expect(skills.length).toBe(1)
|
yield* Effect.promise(() => fs.mkdir(skillDir, { recursive: true }))
|
||||||
const agentSkill = skills.find((s) => s.name === "agent-skill")
|
yield* Effect.promise(() =>
|
||||||
expect(agentSkill).toBeDefined()
|
Bun.write(
|
||||||
expect(agentSkill!.location).toContain(path.join(".agents", "skills", "agent-skill", "SKILL.md"))
|
path.join(skillDir, "SKILL.md"),
|
||||||
},
|
`---
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
test("discovers global skills from ~/.agents/skills/ directory", async () => {
|
|
||||||
await using tmp = await tmpdir({ git: true })
|
|
||||||
|
|
||||||
const originalHome = process.env.OPENCODE_TEST_HOME
|
|
||||||
process.env.OPENCODE_TEST_HOME = tmp.path
|
|
||||||
|
|
||||||
try {
|
|
||||||
const skillDir = path.join(tmp.path, ".agents", "skills", "global-agent-skill")
|
|
||||||
await fs.mkdir(skillDir, { recursive: true })
|
|
||||||
await 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.
|
||||||
---
|
---
|
||||||
@@ -274,119 +280,114 @@ 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.
|
||||||
`,
|
`,
|
||||||
)
|
),
|
||||||
|
)
|
||||||
|
|
||||||
await Instance.provide({
|
yield* Effect.gen(function* () {
|
||||||
directory: tmp.path,
|
const skill = yield* Skill.Service
|
||||||
fn: async () => {
|
const list = yield* skill.all()
|
||||||
const skills = await Skill.all()
|
expect(list.length).toBe(1)
|
||||||
expect(skills.length).toBe(1)
|
expect(list[0].name).toBe("global-agent-skill")
|
||||||
expect(skills[0].name).toBe("global-agent-skill")
|
expect(list[0].description).toBe("A global skill from ~/.agents/skills for testing.")
|
||||||
expect(skills[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"))
|
||||||
expect(skills[0].location).toContain(path.join(".agents", "skills", "global-agent-skill", "SKILL.md"))
|
}).pipe(provideInstance(tmp.path))
|
||||||
},
|
}),
|
||||||
})
|
)
|
||||||
} finally {
|
}),
|
||||||
process.env.OPENCODE_TEST_HOME = originalHome
|
)
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
test("discovers skills from both .claude/skills/ and .agents/skills/", async () => {
|
it.live("discovers skills from both .claude/skills/ and .agents/skills/", () =>
|
||||||
await using tmp = await tmpdir({
|
provideTmpdirInstance(
|
||||||
git: true,
|
(dir) =>
|
||||||
init: async (dir) => {
|
Effect.gen(function* () {
|
||||||
const claudeDir = path.join(dir, ".claude", "skills", "claude-skill")
|
yield* Effect.promise(() =>
|
||||||
const agentDir = path.join(dir, ".agents", "skills", "agent-skill")
|
Promise.all([
|
||||||
await Bun.write(
|
Bun.write(
|
||||||
path.join(claudeDir, "SKILL.md"),
|
path.join(dir, ".claude", "skills", "claude-skill", "SKILL.md"),
|
||||||
`---
|
`---
|
||||||
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
|
||||||
`,
|
`,
|
||||||
)
|
),
|
||||||
await Bun.write(
|
Bun.write(
|
||||||
path.join(agentDir, "SKILL.md"),
|
path.join(dir, ".agents", "skills", "agent-skill", "SKILL.md"),
|
||||||
`---
|
`---
|
||||||
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
|
||||||
`,
|
`,
|
||||||
)
|
),
|
||||||
},
|
]),
|
||||||
})
|
)
|
||||||
|
|
||||||
await Instance.provide({
|
const skill = yield* Skill.Service
|
||||||
directory: tmp.path,
|
const list = yield* skill.all()
|
||||||
fn: async () => {
|
expect(list.length).toBe(2)
|
||||||
const skills = await Skill.all()
|
expect(list.find((x) => x.name === "claude-skill")).toBeDefined()
|
||||||
expect(skills.length).toBe(2)
|
expect(list.find((x) => x.name === "agent-skill")).toBeDefined()
|
||||||
expect(skills.find((s) => s.name === "claude-skill")).toBeDefined()
|
}),
|
||||||
expect(skills.find((s) => s.name === "agent-skill")).toBeDefined()
|
{ git: true },
|
||||||
},
|
),
|
||||||
})
|
)
|
||||||
})
|
|
||||||
|
|
||||||
test("properly resolves directories that skills live in", async () => {
|
it.live("properly resolves directories that skills live in", () =>
|
||||||
await using tmp = await tmpdir({
|
provideTmpdirInstance(
|
||||||
git: true,
|
(dir) =>
|
||||||
init: async (dir) => {
|
Effect.gen(function* () {
|
||||||
const opencodeSkillDir = path.join(dir, ".opencode", "skill", "agent-skill")
|
yield* Effect.promise(() =>
|
||||||
const opencodeSkillsDir = path.join(dir, ".opencode", "skills", "agent-skill")
|
Promise.all([
|
||||||
const claudeDir = path.join(dir, ".claude", "skills", "claude-skill")
|
Bun.write(
|
||||||
const agentDir = path.join(dir, ".agents", "skills", "agent-skill")
|
path.join(dir, ".claude", "skills", "claude-skill", "SKILL.md"),
|
||||||
await Bun.write(
|
`---
|
||||||
path.join(claudeDir, "SKILL.md"),
|
|
||||||
`---
|
|
||||||
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
|
||||||
`,
|
`,
|
||||||
)
|
),
|
||||||
await Bun.write(
|
Bun.write(
|
||||||
path.join(agentDir, "SKILL.md"),
|
path.join(dir, ".agents", "skills", "agent-skill", "SKILL.md"),
|
||||||
`---
|
`---
|
||||||
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
|
||||||
`,
|
`,
|
||||||
)
|
),
|
||||||
await Bun.write(
|
Bun.write(
|
||||||
path.join(opencodeSkillDir, "SKILL.md"),
|
path.join(dir, ".opencode", "skill", "agent-skill", "SKILL.md"),
|
||||||
`---
|
`---
|
||||||
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
|
||||||
`,
|
`,
|
||||||
)
|
),
|
||||||
await Bun.write(
|
Bun.write(
|
||||||
path.join(opencodeSkillsDir, "SKILL.md"),
|
path.join(dir, ".opencode", "skills", "agent-skill", "SKILL.md"),
|
||||||
`---
|
`---
|
||||||
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
|
||||||
`,
|
`,
|
||||||
)
|
),
|
||||||
},
|
]),
|
||||||
})
|
)
|
||||||
|
|
||||||
await Instance.provide({
|
const skill = yield* Skill.Service
|
||||||
directory: tmp.path,
|
expect((yield* skill.dirs()).length).toBe(4)
|
||||||
fn: async () => {
|
}),
|
||||||
const dirs = await Skill.dirs()
|
{ git: true },
|
||||||
expect(dirs.length).toBe(4)
|
),
|
||||||
},
|
)
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user