Files
oh-my-openagent/src/features/opencode-skill-loader/skill-discovery.ts
Willy 6914f2fd04 fix(skills): use directory param instead of process.cwd() for project skill discovery
Project-level skills (.opencode/skills/ and .claude/skills/) were not
discovered in desktop app environments because the discover functions
hardcoded process.cwd() to resolve project paths. In desktop apps,
process.cwd() points to the app installation directory rather than the
user's project directory.

Add optional directory parameter to all project-level skill discovery
functions and thread ctx.directory from the plugin context through the
entire skill loading pipeline. Falls back to process.cwd() when
directory is not provided, preserving CLI compatibility.
2026-02-13 10:49:15 +08:00

77 lines
2.7 KiB
TypeScript

import { createBuiltinSkills } from "../builtin-skills/skills"
import { discoverSkills } from "./loader"
import type { LoadedSkill } from "./types"
import type { SkillResolutionOptions } from "./skill-resolution-options"
const cachedSkillsByProvider = new Map<string, LoadedSkill[]>()
export function clearSkillCache(): void {
cachedSkillsByProvider.clear()
}
export async function getAllSkills(options?: SkillResolutionOptions): Promise<LoadedSkill[]> {
const cacheKey = options?.browserProvider ?? "playwright"
const hasDisabledSkills = options?.disabledSkills && options.disabledSkills.size > 0
// Skip cache if disabledSkills is provided (varies between calls)
if (!hasDisabledSkills) {
const cached = cachedSkillsByProvider.get(cacheKey)
if (cached) return cached
}
const [discoveredSkills, builtinSkillDefinitions] = await Promise.all([
discoverSkills({ includeClaudeCodePaths: true, directory: options?.directory }),
Promise.resolve(
createBuiltinSkills({
browserProvider: options?.browserProvider,
disabledSkills: options?.disabledSkills,
})
),
])
const builtinSkillsAsLoaded: LoadedSkill[] = builtinSkillDefinitions.map((skill) => ({
name: skill.name,
definition: {
name: skill.name,
description: skill.description,
template: skill.template,
model: skill.model,
agent: skill.agent,
subtask: skill.subtask,
},
scope: "builtin" as const,
license: skill.license,
compatibility: skill.compatibility,
metadata: skill.metadata as Record<string, string> | undefined,
allowedTools: skill.allowedTools,
mcpConfig: skill.mcpConfig,
}))
// Provider-gated skill names that should be filtered based on browserProvider
const providerGatedSkillNames = new Set(["agent-browser", "playwright"])
const browserProvider = options?.browserProvider ?? "playwright"
// Filter discovered skills to exclude provider-gated names that don't match the selected provider
const filteredDiscoveredSkills = discoveredSkills.filter((skill) => {
if (!providerGatedSkillNames.has(skill.name)) {
return true
}
// For provider-gated skills, only include if it matches the selected provider
return skill.name === browserProvider
})
const discoveredNames = new Set(filteredDiscoveredSkills.map((skill) => skill.name))
const uniqueBuiltins = builtinSkillsAsLoaded.filter((skill) => !discoveredNames.has(skill.name))
let allSkills = [...filteredDiscoveredSkills, ...uniqueBuiltins]
// Filter discovered skills by disabledSkills (builtin skills are already filtered by createBuiltinSkills)
if (hasDisabledSkills) {
allSkills = allSkills.filter((skill) => !options!.disabledSkills!.has(skill.name))
} else {
cachedSkillsByProvider.set(cacheKey, allSkills)
}
return allSkills
}