Compare commits

...

3 Commits

Author SHA1 Message Date
kitlangton a5fed48b39 refactor(core): scope compatibility sources to skills 2026-08-21 03:04:13 +00:00
kitlangton 13dfb578ad test(core): cover global agents skill discovery 2026-08-21 02:54:27 +00:00
kitlangton 19ffa4b2d9 fix(core): load global compatibility skill sources 2026-08-21 02:01:55 +00:00
2 changed files with 90 additions and 6 deletions
+8 -2
View File
@@ -80,8 +80,14 @@ export const Plugin = define({
if (result.some((item) => Skill.Source.equals(item, source))) return
result.push(source)
}
const claude = loaded.entries.flatMap((entry) => (entry.type === "claude" ? [entry.path] : []))
const agents = loaded.entries.flatMap((entry) => (entry.type === "agents" ? [entry.path] : []))
const claude = [
path.join(global.home, ".claude"),
...loaded.entries.flatMap((entry) => (entry.type === "claude" ? [entry.path] : [])),
]
const agents = [
path.join(global.home, ".agents"),
...loaded.entries.flatMap((entry) => (entry.type === "agents" ? [entry.path] : [])),
]
const directories = loaded.entries.flatMap((entry) => (entry.type === "directory" ? [entry.path] : []))
const items = loaded.entries.flatMap((entry) => (entry.type === "document" ? (entry.info.skills ?? []) : []))
for (const directory of [...claude, ...agents]) {
+82 -4
View File
@@ -175,6 +175,68 @@ metadata:
})
describe("ConfigSkillPlugin.Plugin", () => {
it.live("loads an existing global agents skill through config discovery", () =>
Effect.acquireRelease(
Effect.promise(() => tmpdir()),
(tmp) => Effect.promise(() => tmp[Symbol.asyncDispose]()),
).pipe(
Effect.flatMap((tmp) =>
Effect.gen(function* () {
const global = path.join(tmp.path, "global")
const home = path.join(global, "home")
const directory = path.join(home, "project")
const skills = path.join(home, ".agents", "skills")
yield* Effect.promise(async () => {
await fs.mkdir(path.join(skills, "hello"), { recursive: true })
await fs.mkdir(directory, { recursive: true })
await write(skills, "hello", "Global agents skill")
})
const entries = yield* discover(directory, global)
const skill = yield* startEntries(entries, directory, home)
const hello = (yield* skill.list()).find((item) => item.id === "hello")
expect(hello?.description).toBe("Global agents skill")
expect(hello?.location).toBe(AbsolutePath.make(path.join(skills, "hello", "SKILL.md")))
}),
),
),
)
it.live("loads a global agents skill created after config discovery", () =>
Effect.acquireRelease(
Effect.promise(() => tmpdir()),
(tmp) => Effect.promise(() => tmp[Symbol.asyncDispose]()),
).pipe(
Effect.flatMap((tmp) =>
Effect.gen(function* () {
const global = path.join(tmp.path, "global")
const home = path.join(global, "home")
const directory = path.join(home, "project")
const agents = path.join(home, ".agents")
const skills = path.join(agents, "skills")
yield* Effect.promise(() => fs.mkdir(directory, { recursive: true }))
const entries = yield* discover(directory, global)
const skill = yield* startEntries(entries, directory, home)
const watcher = yield* Watcher.Test
expect(yield* skill.list()).toEqual([])
expect(yield* watcher.subscriptions()).toContainEqual({ path: agents, type: "file" })
yield* Effect.promise(async () => {
await fs.mkdir(path.join(skills, "hello"), { recursive: true })
await write(skills, "hello", "Late global agents skill")
})
yield* emitAndWait({ type: "create", path: agents })
expect((yield* skill.list()).find((item) => item.id === "hello")?.description).toBe(
"Late global agents skill",
)
}),
),
),
)
it.live("maps config entry types to skill directories", () =>
Effect.acquireRelease(
Effect.promise(() => tmpdir()),
@@ -208,7 +270,12 @@ describe("ConfigSkillPlugin.Plugin", () => {
home,
)
const watcher = yield* Watcher.Test
expect(yield* watcher.subscriptions()).toEqual(expected.map((item) => ({ path: item, type: "directory" })))
expect(yield* watcher.subscriptions()).toEqual([
{ path: path.join(home, ".claude"), type: "file" },
{ path: expected[0], type: "directory" },
{ path: path.join(home, ".agents"), type: "file" },
...expected.slice(1).map((item) => ({ path: item, type: "directory" as const })),
])
}),
),
),
@@ -260,8 +327,9 @@ describe("ConfigSkillPlugin.Plugin", () => {
await write(worktreeSkills, "review", "Worktree")
})
const entries = yield* discover(worktree, path.join(tmp.path, "global"))
const skill = yield* startEntries(entries, worktree)
const global = path.join(tmp.path, "global")
const entries = yield* discover(worktree, global)
const skill = yield* startEntries(entries, worktree, path.join(global, "home"))
const review = (yield* skill.list()).find((item) => item.id === "review")
expect(review?.description).toBe("Worktree")
@@ -382,6 +450,8 @@ describe("ConfigSkillPlugin.Plugin", () => {
const watcher = yield* Watcher.Test
expect((yield* skill.list()).find((item) => item.id === "bro")?.description).toBe("First")
expect(yield* watcher.subscriptions()).toEqual([
{ path: path.join(tmp.path, ".claude"), type: "file" },
{ path: path.join(tmp.path, ".agents"), type: "file" },
{ path: first, type: "directory" },
{ path: source, type: "file" },
])
@@ -394,8 +464,12 @@ describe("ConfigSkillPlugin.Plugin", () => {
expect((yield* skill.list()).find((item) => item.id === "bro")?.description).toBe("Second")
expect(yield* watcher.subscriptions()).toEqual([
{ path: path.join(tmp.path, ".claude"), type: "file" },
{ path: path.join(tmp.path, ".agents"), type: "file" },
{ path: first, type: "directory" },
{ path: source, type: "file" },
{ path: path.join(tmp.path, ".claude"), type: "file" },
{ path: path.join(tmp.path, ".agents"), type: "file" },
{ path: second, type: "directory" },
{ path: source, type: "file" },
])
@@ -415,7 +489,11 @@ describe("ConfigSkillPlugin.Plugin", () => {
const skill = yield* start([source], tmp.path)
const watcher = yield* Watcher.Test
expect(yield* skill.list()).toEqual([])
expect(yield* watcher.subscriptions()).toEqual([{ path: path.join(tmp.path, "generated"), type: "file" }])
expect(yield* watcher.subscriptions()).toEqual([
{ path: path.join(tmp.path, ".claude"), type: "file" },
{ path: path.join(tmp.path, ".agents"), type: "file" },
{ path: path.join(tmp.path, "generated"), type: "file" },
])
yield* Effect.promise(() => fs.mkdir(path.join(tmp.path, "generated")))
yield* emitAndWait({ type: "create", path: path.join(tmp.path, "generated") })