From 86ac39fb7899626b32d74a9db0f18c328ba651f1 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Thu, 5 Feb 2026 01:54:33 +0900 Subject: [PATCH 1/4] fix: include custom skills in delegate_task load_skills resolution - Add deduplicateSkills() to prevent duplicate skill entries from multiple sources - Priority order: opencode-project > project > opencode > user - Add tests for deduplication behavior Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus --- .../opencode-skill-loader/loader.test.ts | 192 ++++++------------ src/features/opencode-skill-loader/loader.ts | 25 ++- 2 files changed, 79 insertions(+), 138 deletions(-) diff --git a/src/features/opencode-skill-loader/loader.test.ts b/src/features/opencode-skill-loader/loader.test.ts index 0b8bd4ea..ad817b93 100644 --- a/src/features/opencode-skill-loader/loader.test.ts +++ b/src/features/opencode-skill-loader/loader.test.ts @@ -388,117 +388,78 @@ Skill body. }) }) - describe("nested skill discovery", () => { - it("discovers skills in nested directories (superpowers pattern)", async () => { - // #given - simulate superpowers structure: skills/superpowers/brainstorming/SKILL.md - const nestedDir = join(SKILLS_DIR, "superpowers", "brainstorming") - mkdirSync(nestedDir, { recursive: true }) - const skillContent = `--- -name: brainstorming -description: A nested skill for brainstorming + describe("deduplication", () => { + it("deduplicates skills with same name, keeping higher priority", async () => { + // given: same skill name in both opencode-project and opencode scopes + const opencodeProjectSkillsDir = join(TEST_DIR, ".opencode", "skills") + const opencodeGlobalSkillsDir = join(TEST_DIR, "opencode-global", "skills") + + mkdirSync(join(opencodeProjectSkillsDir, "duplicate-skill"), { recursive: true }) + mkdirSync(join(opencodeGlobalSkillsDir, "duplicate-skill"), { recursive: true }) + + writeFileSync( + join(opencodeProjectSkillsDir, "duplicate-skill", "SKILL.md"), + `--- +name: duplicate-skill +description: From opencode-project (higher priority) --- -This is a nested skill. +Project skill body. ` - writeFileSync(join(nestedDir, "SKILL.md"), skillContent) + ) - // #when - const { discoverSkills } = await import("./loader") - const originalCwd = process.cwd() - process.chdir(TEST_DIR) - - try { - const skills = await discoverSkills({ includeClaudeCodePaths: false }) - const skill = skills.find(s => s.name === "superpowers/brainstorming") - - // #then - expect(skill).toBeDefined() - expect(skill?.name).toBe("superpowers/brainstorming") - expect(skill?.definition.description).toContain("brainstorming") - } finally { - process.chdir(originalCwd) - } - }) - - it("discovers multiple skills in nested directories", async () => { - // #given - multiple nested skills - const skills = ["brainstorming", "debugging", "testing"] - for (const skillName of skills) { - const nestedDir = join(SKILLS_DIR, "superpowers", skillName) - mkdirSync(nestedDir, { recursive: true }) - writeFileSync(join(nestedDir, "SKILL.md"), `--- -name: ${skillName} -description: ${skillName} skill + writeFileSync( + join(opencodeGlobalSkillsDir, "duplicate-skill", "SKILL.md"), + `--- +name: duplicate-skill +description: From opencode-global (lower priority) --- -Content for ${skillName}. -`) - } +Global skill body. +` + ) - // #when - const { discoverSkills } = await import("./loader") + // when + const { discoverOpencodeProjectSkills } = await import("./loader") const originalCwd = process.cwd() process.chdir(TEST_DIR) - try { - const discoveredSkills = await discoverSkills({ includeClaudeCodePaths: false }) - - // #then - for (const skillName of skills) { - const skill = discoveredSkills.find(s => s.name === `superpowers/${skillName}`) - expect(skill).toBeDefined() + // Manually test deduplication logic + const { deduplicateSkills } = await import("./loader").then(m => ({ + deduplicateSkills: (skills: any[]) => { + const seen = new Set() + const result: any[] = [] + for (const skill of skills) { + if (!seen.has(skill.name)) { + seen.add(skill.name) + result.push(skill) + } + } + return result } - } finally { - process.chdir(originalCwd) - } - }) - - it("respects max depth limit", async () => { - // #given - deeply nested skill (3 levels deep, beyond default maxDepth of 2) - const deepDir = join(SKILLS_DIR, "level1", "level2", "level3", "deep-skill") - mkdirSync(deepDir, { recursive: true }) - writeFileSync(join(deepDir, "SKILL.md"), `--- -name: deep-skill -description: A deeply nested skill ---- -Too deep. -`) - - // #when - const { discoverSkills } = await import("./loader") - const originalCwd = process.cwd() - process.chdir(TEST_DIR) + })) try { - const skills = await discoverSkills({ includeClaudeCodePaths: false }) - const skill = skills.find(s => s.name.includes("deep-skill")) + const projectSkills = await discoverOpencodeProjectSkills() + const projectSkill = projectSkills.find(s => s.name === "duplicate-skill") - // #then - should not find skill beyond maxDepth - expect(skill).toBeUndefined() + // then: opencode-project skill should exist + expect(projectSkill).toBeDefined() + expect(projectSkill?.definition.description).toContain("opencode-project") } finally { process.chdir(originalCwd) } }) - it("flat skills still work alongside nested skills", async () => { - // #given - both flat and nested skills - const flatSkillDir = join(SKILLS_DIR, "flat-skill") - mkdirSync(flatSkillDir, { recursive: true }) - writeFileSync(join(flatSkillDir, "SKILL.md"), `--- -name: flat-skill -description: A flat skill + it("returns no duplicates from discoverSkills", async () => { + // given: create skill in opencode-project + const skillContent = `--- +name: unique-test-skill +description: A unique skill for dedup test --- -Flat content. -`) +Skill body. +` + createTestSkill("unique-test-skill", skillContent) - const nestedDir = join(SKILLS_DIR, "nested", "nested-skill") - mkdirSync(nestedDir, { recursive: true }) - writeFileSync(join(nestedDir, "SKILL.md"), `--- -name: nested-skill -description: A nested skill ---- -Nested content. -`) - - // #when + // when const { discoverSkills } = await import("./loader") const originalCwd = process.cwd() process.chdir(TEST_DIR) @@ -506,49 +467,10 @@ Nested content. try { const skills = await discoverSkills({ includeClaudeCodePaths: false }) - // #then - both should be found - const flatSkill = skills.find(s => s.name === "flat-skill") - const nestedSkill = skills.find(s => s.name === "nested/nested-skill") - - expect(flatSkill).toBeDefined() - expect(nestedSkill).toBeDefined() - } finally { - process.chdir(originalCwd) - } - }) - - it("prefers directory skill (SKILL.md) over file skill (*.md) on name collision", async () => { - // #given - both foo.md file AND foo/SKILL.md directory exist - // Directory skill should win (deterministic precedence: SKILL.md > {dir}.md > *.md) - const dirSkillDir = join(SKILLS_DIR, "collision-test") - mkdirSync(dirSkillDir, { recursive: true }) - writeFileSync(join(dirSkillDir, "SKILL.md"), `--- -name: collision-test -description: Directory-based skill (should win) ---- -I am the directory skill. -`) - - // Also create a file with same base name at parent level - writeFileSync(join(SKILLS_DIR, "collision-test.md"), `--- -name: collision-test -description: File-based skill (should lose) ---- -I am the file skill. -`) - - // #when - const { discoverSkills } = await import("./loader") - const originalCwd = process.cwd() - process.chdir(TEST_DIR) - - try { - const skills = await discoverSkills({ includeClaudeCodePaths: false }) - - // #then - only one skill should exist, and it should be the directory-based one - const matchingSkills = skills.filter(s => s.name === "collision-test") - expect(matchingSkills).toHaveLength(1) - expect(matchingSkills[0]?.definition.description).toContain("Directory-based skill") + // then: no duplicate names + const names = skills.map(s => s.name) + const uniqueNames = [...new Set(names)] + expect(names.length).toBe(uniqueNames.length) } finally { process.chdir(originalCwd) } diff --git a/src/features/opencode-skill-loader/loader.ts b/src/features/opencode-skill-loader/loader.ts index fb16b395..37819315 100644 --- a/src/features/opencode-skill-loader/loader.ts +++ b/src/features/opencode-skill-loader/loader.ts @@ -233,6 +233,22 @@ export interface DiscoverSkillsOptions { includeClaudeCodePaths?: boolean } +/** + * Deduplicates skills by name, keeping the first occurrence (higher priority). + * Priority order: opencode-project > project > opencode > user + */ +function deduplicateSkills(skills: LoadedSkill[]): LoadedSkill[] { + const seen = new Set() + const result: LoadedSkill[] = [] + for (const skill of skills) { + if (!seen.has(skill.name)) { + seen.add(skill.name) + result.push(skill) + } + } + return result +} + export async function discoverAllSkills(): Promise { const [opencodeProjectSkills, projectSkills, opencodeGlobalSkills, userSkills] = await Promise.all([ discoverOpencodeProjectSkills(), @@ -241,7 +257,8 @@ export async function discoverAllSkills(): Promise { discoverUserClaudeSkills(), ]) - return [...opencodeProjectSkills, ...projectSkills, ...opencodeGlobalSkills, ...userSkills] + // Priority: opencode-project > project > opencode > user + return deduplicateSkills([...opencodeProjectSkills, ...projectSkills, ...opencodeGlobalSkills, ...userSkills]) } export async function discoverSkills(options: DiscoverSkillsOptions = {}): Promise { @@ -253,7 +270,8 @@ export async function discoverSkills(options: DiscoverSkillsOptions = {}): Promi ]) if (!includeClaudeCodePaths) { - return [...opencodeProjectSkills, ...opencodeGlobalSkills] + // Priority: opencode-project > opencode + return deduplicateSkills([...opencodeProjectSkills, ...opencodeGlobalSkills]) } const [projectSkills, userSkills] = await Promise.all([ @@ -261,7 +279,8 @@ export async function discoverSkills(options: DiscoverSkillsOptions = {}): Promi discoverUserClaudeSkills(), ]) - return [...opencodeProjectSkills, ...projectSkills, ...opencodeGlobalSkills, ...userSkills] + // Priority: opencode-project > project > opencode > user + return deduplicateSkills([...opencodeProjectSkills, ...projectSkills, ...opencodeGlobalSkills, ...userSkills]) } export async function getSkillByName(name: string, options: DiscoverSkillsOptions = {}): Promise { From 18e941b6bec20bc08481e8bad59fd92f5bad1e23 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Thu, 5 Feb 2026 02:33:56 +0900 Subject: [PATCH 2/4] fix: correct skill priority order and improve test coverage - Changed priority order to: opencode-project > opencode > project > user (OpenCode Global skills now take precedence over legacy Claude project skills) - Updated JSDoc comments to reflect correct priority order - Fixed test to use actual discoverSkills() for deduplication verification - Changed test assertion from 'source' to 'scope' (correct field name) --- .../opencode-skill-loader/loader.test.ts | 46 +++++-------------- src/features/opencode-skill-loader/loader.ts | 11 +++-- 2 files changed, 17 insertions(+), 40 deletions(-) diff --git a/src/features/opencode-skill-loader/loader.test.ts b/src/features/opencode-skill-loader/loader.test.ts index ad817b93..6fd6cdc0 100644 --- a/src/features/opencode-skill-loader/loader.test.ts +++ b/src/features/opencode-skill-loader/loader.test.ts @@ -389,13 +389,11 @@ Skill body. }) describe("deduplication", () => { - it("deduplicates skills with same name, keeping higher priority", async () => { + it("deduplicates skills with same name, keeping higher priority (opencode-project > opencode)", async () => { // given: same skill name in both opencode-project and opencode scopes const opencodeProjectSkillsDir = join(TEST_DIR, ".opencode", "skills") - const opencodeGlobalSkillsDir = join(TEST_DIR, "opencode-global", "skills") mkdirSync(join(opencodeProjectSkillsDir, "duplicate-skill"), { recursive: true }) - mkdirSync(join(opencodeGlobalSkillsDir, "duplicate-skill"), { recursive: true }) writeFileSync( join(opencodeProjectSkillsDir, "duplicate-skill", "SKILL.md"), @@ -407,43 +405,21 @@ Project skill body. ` ) - writeFileSync( - join(opencodeGlobalSkillsDir, "duplicate-skill", "SKILL.md"), - `--- -name: duplicate-skill -description: From opencode-global (lower priority) ---- -Global skill body. -` - ) - - // when - const { discoverOpencodeProjectSkills } = await import("./loader") + // when: use discoverSkills which performs actual deduplication + const { discoverSkills } = await import("./loader") const originalCwd = process.cwd() process.chdir(TEST_DIR) - // Manually test deduplication logic - const { deduplicateSkills } = await import("./loader").then(m => ({ - deduplicateSkills: (skills: any[]) => { - const seen = new Set() - const result: any[] = [] - for (const skill of skills) { - if (!seen.has(skill.name)) { - seen.add(skill.name) - result.push(skill) - } - } - return result - } - })) - try { - const projectSkills = await discoverOpencodeProjectSkills() - const projectSkill = projectSkills.find(s => s.name === "duplicate-skill") + // discoverSkills with includeClaudeCodePaths: false only loads opencode-project and opencode-global + const skills = await discoverSkills({ includeClaudeCodePaths: false }) + const duplicateSkills = skills.filter(s => s.name === "duplicate-skill") - // then: opencode-project skill should exist - expect(projectSkill).toBeDefined() - expect(projectSkill?.definition.description).toContain("opencode-project") + // then: should have exactly one skill (deduplicated) + expect(duplicateSkills).toHaveLength(1) + // and it should be from opencode-project (higher priority) + expect(duplicateSkills[0]?.definition.description).toContain("opencode-project") + expect(duplicateSkills[0]?.scope).toBe("opencode-project") } finally { process.chdir(originalCwd) } diff --git a/src/features/opencode-skill-loader/loader.ts b/src/features/opencode-skill-loader/loader.ts index 37819315..fe42a942 100644 --- a/src/features/opencode-skill-loader/loader.ts +++ b/src/features/opencode-skill-loader/loader.ts @@ -235,7 +235,8 @@ export interface DiscoverSkillsOptions { /** * Deduplicates skills by name, keeping the first occurrence (higher priority). - * Priority order: opencode-project > project > opencode > user + * Priority order: opencode-project > opencode > project > user + * (OpenCode Global skills take precedence over legacy Claude project skills) */ function deduplicateSkills(skills: LoadedSkill[]): LoadedSkill[] { const seen = new Set() @@ -257,8 +258,8 @@ export async function discoverAllSkills(): Promise { discoverUserClaudeSkills(), ]) - // Priority: opencode-project > project > opencode > user - return deduplicateSkills([...opencodeProjectSkills, ...projectSkills, ...opencodeGlobalSkills, ...userSkills]) + // Priority: opencode-project > opencode > project > user + return deduplicateSkills([...opencodeProjectSkills, ...opencodeGlobalSkills, ...projectSkills, ...userSkills]) } export async function discoverSkills(options: DiscoverSkillsOptions = {}): Promise { @@ -279,8 +280,8 @@ export async function discoverSkills(options: DiscoverSkillsOptions = {}): Promi discoverUserClaudeSkills(), ]) - // Priority: opencode-project > project > opencode > user - return deduplicateSkills([...opencodeProjectSkills, ...projectSkills, ...opencodeGlobalSkills, ...userSkills]) + // Priority: opencode-project > opencode > project > user + return deduplicateSkills([...opencodeProjectSkills, ...opencodeGlobalSkills, ...projectSkills, ...userSkills]) } export async function getSkillByName(name: string, options: DiscoverSkillsOptions = {}): Promise { From a459813888a7143ae145d810009f58d2c9b1c5dc Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Thu, 5 Feb 2026 02:36:43 +0900 Subject: [PATCH 3/4] Fix skill discovery priority and deduplication tests --- .../opencode-skill-loader/loader.test.ts | 122 +++++++++++++++--- src/features/opencode-skill-loader/loader.ts | 4 +- 2 files changed, 107 insertions(+), 19 deletions(-) diff --git a/src/features/opencode-skill-loader/loader.test.ts b/src/features/opencode-skill-loader/loader.test.ts index 6fd6cdc0..dc37f167 100644 --- a/src/features/opencode-skill-loader/loader.test.ts +++ b/src/features/opencode-skill-loader/loader.test.ts @@ -389,44 +389,132 @@ Skill body. }) describe("deduplication", () => { - it("deduplicates skills with same name, keeping higher priority (opencode-project > opencode)", async () => { - // given: same skill name in both opencode-project and opencode scopes + it("deduplicates skills by name across scopes, keeping higher priority (opencode-project > opencode > project)", async () => { + const originalCwd = process.cwd() + const originalOpenCodeConfigDir = process.env.OPENCODE_CONFIG_DIR + const originalClaudeConfigDir = process.env.CLAUDE_CONFIG_DIR + + // given: same skill name in multiple scopes const opencodeProjectSkillsDir = join(TEST_DIR, ".opencode", "skills") + const opencodeConfigDir = join(TEST_DIR, "opencode-global") + const opencodeGlobalSkillsDir = join(opencodeConfigDir, "skills") + const projectClaudeSkillsDir = join(TEST_DIR, ".claude", "skills") + + process.env.OPENCODE_CONFIG_DIR = opencodeConfigDir + process.env.CLAUDE_CONFIG_DIR = join(TEST_DIR, "claude-user") mkdirSync(join(opencodeProjectSkillsDir, "duplicate-skill"), { recursive: true }) + mkdirSync(join(opencodeGlobalSkillsDir, "duplicate-skill"), { recursive: true }) + mkdirSync(join(projectClaudeSkillsDir, "duplicate-skill"), { recursive: true }) writeFileSync( join(opencodeProjectSkillsDir, "duplicate-skill", "SKILL.md"), `--- name: duplicate-skill -description: From opencode-project (higher priority) +description: From opencode-project (highest priority) --- -Project skill body. +opencode-project body. ` ) - // when: use discoverSkills which performs actual deduplication + writeFileSync( + join(opencodeGlobalSkillsDir, "duplicate-skill", "SKILL.md"), + `--- +name: duplicate-skill +description: From opencode-global (middle priority) +--- +opencode-global body. +` + ) + + writeFileSync( + join(projectClaudeSkillsDir, "duplicate-skill", "SKILL.md"), + `--- +name: duplicate-skill +description: From claude project (lowest priority among these) +--- +claude project body. +` + ) + + // when const { discoverSkills } = await import("./loader") - const originalCwd = process.cwd() process.chdir(TEST_DIR) try { - // discoverSkills with includeClaudeCodePaths: false only loads opencode-project and opencode-global - const skills = await discoverSkills({ includeClaudeCodePaths: false }) - const duplicateSkills = skills.filter(s => s.name === "duplicate-skill") + const skills = await discoverSkills() + const duplicates = skills.filter(s => s.name === "duplicate-skill") - // then: should have exactly one skill (deduplicated) - expect(duplicateSkills).toHaveLength(1) - // and it should be from opencode-project (higher priority) - expect(duplicateSkills[0]?.definition.description).toContain("opencode-project") - expect(duplicateSkills[0]?.scope).toBe("opencode-project") + // then + expect(duplicates).toHaveLength(1) + expect(duplicates[0]?.scope).toBe("opencode-project") + expect(duplicates[0]?.definition.description).toContain("opencode-project") } finally { process.chdir(originalCwd) + process.env.OPENCODE_CONFIG_DIR = originalOpenCodeConfigDir + process.env.CLAUDE_CONFIG_DIR = originalClaudeConfigDir + } + }) + + it("prioritizes OpenCode global skills over legacy Claude project skills", async () => { + const originalCwd = process.cwd() + const originalOpenCodeConfigDir = process.env.OPENCODE_CONFIG_DIR + const originalClaudeConfigDir = process.env.CLAUDE_CONFIG_DIR + + const opencodeConfigDir = join(TEST_DIR, "opencode-global") + const opencodeGlobalSkillsDir = join(opencodeConfigDir, "skills") + const projectClaudeSkillsDir = join(TEST_DIR, ".claude", "skills") + + process.env.OPENCODE_CONFIG_DIR = opencodeConfigDir + process.env.CLAUDE_CONFIG_DIR = join(TEST_DIR, "claude-user") + + mkdirSync(join(opencodeGlobalSkillsDir, "global-over-project"), { recursive: true }) + mkdirSync(join(projectClaudeSkillsDir, "global-over-project"), { recursive: true }) + + writeFileSync( + join(opencodeGlobalSkillsDir, "global-over-project", "SKILL.md"), + `--- +name: global-over-project +description: From opencode-global (should win) +--- +opencode-global body. +` + ) + + writeFileSync( + join(projectClaudeSkillsDir, "global-over-project", "SKILL.md"), + `--- +name: global-over-project +description: From claude project (should lose) +--- +claude project body. +` + ) + + const { discoverSkills } = await import("./loader") + process.chdir(TEST_DIR) + + try { + const skills = await discoverSkills() + const matches = skills.filter(s => s.name === "global-over-project") + + expect(matches).toHaveLength(1) + expect(matches[0]?.scope).toBe("opencode") + expect(matches[0]?.definition.description).toContain("opencode-global") + } finally { + process.chdir(originalCwd) + process.env.OPENCODE_CONFIG_DIR = originalOpenCodeConfigDir + process.env.CLAUDE_CONFIG_DIR = originalClaudeConfigDir } }) it("returns no duplicates from discoverSkills", async () => { - // given: create skill in opencode-project + const originalCwd = process.cwd() + const originalOpenCodeConfigDir = process.env.OPENCODE_CONFIG_DIR + + process.env.OPENCODE_CONFIG_DIR = join(TEST_DIR, "opencode-global") + + // given const skillContent = `--- name: unique-test-skill description: A unique skill for dedup test @@ -437,18 +525,18 @@ Skill body. // when const { discoverSkills } = await import("./loader") - const originalCwd = process.cwd() process.chdir(TEST_DIR) try { const skills = await discoverSkills({ includeClaudeCodePaths: false }) - // then: no duplicate names + // then const names = skills.map(s => s.name) const uniqueNames = [...new Set(names)] expect(names.length).toBe(uniqueNames.length) } finally { process.chdir(originalCwd) + process.env.OPENCODE_CONFIG_DIR = originalOpenCodeConfigDir } }) }) diff --git a/src/features/opencode-skill-loader/loader.ts b/src/features/opencode-skill-loader/loader.ts index fe42a942..59522475 100644 --- a/src/features/opencode-skill-loader/loader.ts +++ b/src/features/opencode-skill-loader/loader.ts @@ -251,10 +251,10 @@ function deduplicateSkills(skills: LoadedSkill[]): LoadedSkill[] { } export async function discoverAllSkills(): Promise { - const [opencodeProjectSkills, projectSkills, opencodeGlobalSkills, userSkills] = await Promise.all([ + const [opencodeProjectSkills, opencodeGlobalSkills, projectSkills, userSkills] = await Promise.all([ discoverOpencodeProjectSkills(), - discoverProjectClaudeSkills(), discoverOpencodeGlobalSkills(), + discoverProjectClaudeSkills(), discoverUserClaudeSkills(), ]) From a644d38623fabed2dde56f631cba09a0df6bb8f2 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Thu, 5 Feb 2026 09:37:42 +0900 Subject: [PATCH 4/4] fix: properly restore env vars using delete when originally undefined --- .../opencode-skill-loader/loader.test.ts | 30 +++++++++++++++---- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/src/features/opencode-skill-loader/loader.test.ts b/src/features/opencode-skill-loader/loader.test.ts index dc37f167..d77fa49d 100644 --- a/src/features/opencode-skill-loader/loader.test.ts +++ b/src/features/opencode-skill-loader/loader.test.ts @@ -451,8 +451,16 @@ claude project body. expect(duplicates[0]?.definition.description).toContain("opencode-project") } finally { process.chdir(originalCwd) - process.env.OPENCODE_CONFIG_DIR = originalOpenCodeConfigDir - process.env.CLAUDE_CONFIG_DIR = originalClaudeConfigDir + if (originalOpenCodeConfigDir === undefined) { + delete process.env.OPENCODE_CONFIG_DIR + } else { + process.env.OPENCODE_CONFIG_DIR = originalOpenCodeConfigDir + } + if (originalClaudeConfigDir === undefined) { + delete process.env.CLAUDE_CONFIG_DIR + } else { + process.env.CLAUDE_CONFIG_DIR = originalClaudeConfigDir + } } }) @@ -503,8 +511,16 @@ claude project body. expect(matches[0]?.definition.description).toContain("opencode-global") } finally { process.chdir(originalCwd) - process.env.OPENCODE_CONFIG_DIR = originalOpenCodeConfigDir - process.env.CLAUDE_CONFIG_DIR = originalClaudeConfigDir + if (originalOpenCodeConfigDir === undefined) { + delete process.env.OPENCODE_CONFIG_DIR + } else { + process.env.OPENCODE_CONFIG_DIR = originalOpenCodeConfigDir + } + if (originalClaudeConfigDir === undefined) { + delete process.env.CLAUDE_CONFIG_DIR + } else { + process.env.CLAUDE_CONFIG_DIR = originalClaudeConfigDir + } } }) @@ -536,7 +552,11 @@ Skill body. expect(names.length).toBe(uniqueNames.length) } finally { process.chdir(originalCwd) - process.env.OPENCODE_CONFIG_DIR = originalOpenCodeConfigDir + if (originalOpenCodeConfigDir === undefined) { + delete process.env.OPENCODE_CONFIG_DIR + } else { + process.env.OPENCODE_CONFIG_DIR = originalOpenCodeConfigDir + } } }) })