diff --git a/packages/core/src/config.ts b/packages/core/src/config.ts index 55ccbaf834a..86a60e10da7 100644 --- a/packages/core/src/config.ts +++ b/packages/core/src/config.ts @@ -225,13 +225,13 @@ export const layer = (options?: Options) => // We load certain files from a few other folders in the ecosystem const claude = [ ...new Set([ - globalClaudeDirectory, + ...((yield* fs.isDir(globalClaudeDirectory)) ? [globalClaudeDirectory] : []), ...discovered.filter((item) => path.basename(item) === ".claude").toReversed(), ]), ].map((directory) => new ClaudeDirectory({ type: "claude", path: AbsolutePath.make(directory) })) const agents = [ ...new Set([ - globalAgentsDirectory, + ...((yield* fs.isDir(globalAgentsDirectory)) ? [globalAgentsDirectory] : []), ...discovered.filter((item) => path.basename(item) === ".agents").toReversed(), ]), ].map((directory) => new AgentsDirectory({ type: "agents", path: AbsolutePath.make(directory) })) diff --git a/packages/core/src/config/plugin/skill.ts b/packages/core/src/config/plugin/skill.ts index d263fbc420b..9dbc81544c1 100644 --- a/packages/core/src/config/plugin/skill.ts +++ b/packages/core/src/config/plugin/skill.ts @@ -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]) { diff --git a/packages/core/test/config/config.test.ts b/packages/core/test/config/config.test.ts index 0b1a7b52ca3..d27fcf4fe8d 100644 --- a/packages/core/test/config/config.test.ts +++ b/packages/core/test/config/config.test.ts @@ -4,7 +4,7 @@ import { describe, expect } from "bun:test" import { Effect, Fiber, Layer, Logger, PubSub, Schema, Stream } from "effect" import { FastCheck } from "effect/testing" import { Config } from "@opencode-ai/core/config" -import { AgentsDirectory, ClaudeDirectory, Directory, Document, Event, Info } from "@opencode-ai/schema/config" +import { AgentsDirectory, Directory, Document, Event, Info } from "@opencode-ai/schema/config" import { ConfigModel } from "@opencode-ai/schema/config/model" import { ConfigProvider } from "@opencode-ai/schema/config/provider" import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder" @@ -793,7 +793,7 @@ describe("Config", () => { }), ) - it.live("includes global compatibility directories before they exist", () => + it.live("returns an empty configuration when directory files do not exist", () => Effect.acquireRelease( Effect.promise(() => tmpdir()), (tmp) => Effect.promise(() => tmp[Symbol.asyncDispose]()), @@ -804,14 +804,6 @@ describe("Config", () => { const entries = yield* config.entries() expect(entries).toEqual([ - new ClaudeDirectory({ - type: "claude", - path: AbsolutePath.make(path.join(tmp.path, "global", "home", ".claude")), - }), - new AgentsDirectory({ - type: "agents", - path: AbsolutePath.make(path.join(tmp.path, "global", "home", ".agents")), - }), new Directory({ type: "directory", path: AbsolutePath.make(path.join(tmp.path, "global")) }), ]) }).pipe(Effect.provide(testLayer(tmp.path))), diff --git a/packages/core/test/config/skill.test.ts b/packages/core/test/config/skill.test.ts index 89d38b6b26e..31ab1c2d2fd 100644 --- a/packages/core/test/config/skill.test.ts +++ b/packages/core/test/config/skill.test.ts @@ -270,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 })), + ]) }), ), ), @@ -322,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") @@ -444,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" }, ]) @@ -456,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" }, ]) @@ -477,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") })