///
import { describe, expect, it } from "bun:test"
import type { LoadedSkill } from "../../features/opencode-skill-loader/types"
function requireFresh(modulePath: string): T {
const resolvedPath = require.resolve(modulePath)
if (require.cache?.[resolvedPath]) {
delete require.cache[resolvedPath]
}
return require(modulePath) as T
}
function createSkillTool(...args: Parameters): ReturnType {
return requireFresh("./tools").createSkillTool(...args)
}
function createMockSkill(name: string): LoadedSkill {
return {
name,
path: `/test/skills/${name}/SKILL.md`,
resolvedPath: `/test/skills/${name}`,
definition: {
name,
description: `Test skill ${name}`,
template: `Test skill template for ${name}`,
},
scope: "opencode-project",
}
}
async function waitForRefresh(predicate: () => boolean): Promise {
for (let attempt = 0; attempt < 200; attempt += 1) {
if (predicate()) {
return
}
await new Promise((resolve) => setTimeout(resolve, 10))
}
throw new Error("Timed out waiting for async skill description refresh")
}
describe("skill tool - async native skill description refresh", () => {
it("updates description after async native skills resolve", async () => {
//#given
let allCallCount = 0
const tool = createSkillTool({
skills: [createMockSkill("seeded-skill")],
commands: [],
nativeSkills: {
async all() {
allCallCount += 1
return [{
name: "async-native-skill",
description: "Async native skill from plugin input",
location: "/external/skills/async-native-skill/SKILL.md",
content: "Async native skill body",
}]
},
async get() {
return undefined
},
async dirs() {
return []
},
},
})
expect(tool.description).toContain("seeded-skill")
expect(tool.description).not.toContain("async-native-skill")
//#when
await waitForRefresh(() => tool.description.includes("async-native-skill"))
//#then
expect(allCallCount).toBeGreaterThanOrEqual(1)
expect(tool.description).toContain("seeded-skill")
expect(tool.description).toContain("async-native-skill")
})
})