From 30f4d30d7cf7171ad0650997ae118a2aee67eed3 Mon Sep 17 00:00:00 2001 From: dbpolito Date: Fri, 27 Mar 2026 17:40:12 -0300 Subject: [PATCH] fix: preserve configured skill paths - keep normalized custom skill paths when bundled skills are unavailable - only register bundled skills when a valid bundled root exists - cover missing bundled root behavior in skills config tests --- packages/opencode/config.ts | 22 +++++++++++--------- packages/opencode/test/skills-config.test.ts | 14 +++++++++++++ 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/packages/opencode/config.ts b/packages/opencode/config.ts index f00ff9c..059b023 100644 --- a/packages/opencode/config.ts +++ b/packages/opencode/config.ts @@ -49,23 +49,20 @@ async function hasBundledSkillDirectories(root: string): Promise { } async function resolveBundledSkillsRoot(): Promise { - let firstExistingCandidate: string | undefined; - for (const candidate of BUNDLED_SKILL_ROOT_CANDIDATES) { if (!await pathExists(candidate)) continue; - firstExistingCandidate ??= candidate; - if (await hasBundledSkillDirectories(candidate)) { return candidate; } } - return firstExistingCandidate; + return undefined; } type ApplyConfigOptions = { logger?: PluginLogger; + resolveBundledSkillsRoot?: () => Promise; }; function normalizeSkillPaths(paths: unknown): string[] { @@ -145,23 +142,28 @@ export async function applyCommandsConfig( } export async function applySkillsConfig(cfg: Config, options?: ApplyConfigOptions) { - const bundledSkillsRoot = await resolveBundledSkillsRoot(); + const bundledSkillsRoot = options?.resolveBundledSkillsRoot + ? await options.resolveBundledSkillsRoot() + : await resolveBundledSkillsRoot(); + const skillsConfig = cfg as ConfigWithSkillsPaths; + const normalizedPaths = normalizeSkillPaths(skillsConfig.skills?.paths); if (!bundledSkillsRoot) { await options?.logger?.warn("Skipping Kompass skills registration", { reason: "No bundled skills directory found", }); - const skillsConfig = cfg as ConfigWithSkillsPaths; - if (skillsConfig.skills && "paths" in skillsConfig.skills) { + if (normalizedPaths.length > 0) { + skillsConfig.skills ??= {}; + skillsConfig.skills.paths = normalizedPaths; + } else if (skillsConfig.skills && "paths" in skillsConfig.skills) { delete skillsConfig.skills.paths; } return; } - const skillsConfig = cfg as ConfigWithSkillsPaths; skillsConfig.skills ??= {}; - skillsConfig.skills.paths = normalizeSkillPaths(skillsConfig.skills.paths); + skillsConfig.skills.paths = normalizedPaths; if (!skillsConfig.skills.paths.includes(bundledSkillsRoot)) { skillsConfig.skills.paths.push(bundledSkillsRoot); diff --git a/packages/opencode/test/skills-config.test.ts b/packages/opencode/test/skills-config.test.ts index 0f3861b..d2a5bfe 100644 --- a/packages/opencode/test/skills-config.test.ts +++ b/packages/opencode/test/skills-config.test.ts @@ -42,4 +42,18 @@ describe("applySkillsConfig", () => { assert.equal(cfg.skills?.paths?.length, 2); assert.equal(typeof cfg.skills?.paths?.[1], "string"); }); + + test("preserves valid configured skill paths when no bundled skills root exists", async () => { + const cfg: { skills?: { paths?: unknown[] } } = { + skills: { + paths: [undefined, "", "/tmp/custom-skills"], + }, + }; + + await applySkillsConfig(cfg as never, { + resolveBundledSkillsRoot: async () => undefined, + }); + + assert.deepEqual(cfg.skills?.paths, ["/tmp/custom-skills"]); + }); });