YeonGyu-Kim 51ced65b5f refactor(opencode-skill-loader): split loader and merger into focused modules
Extract skill loading pipeline into single-responsibility modules:
- skill-discovery.ts, skill-directory-loader.ts, skill-deduplication.ts
- loaded-skill-from-path.ts, loaded-skill-template-extractor.ts
- skill-template-resolver.ts, skill-definition-record.ts
- git-master-template-injection.ts, allowed-tools-parser.ts
- skill-mcp-config.ts, skill-resolution-options.ts
- merger/ directory for skill merging logic
2026-02-08 16:21:19 +09:00

98 lines
4.2 KiB
TypeScript

import { join } from "path"
import { getClaudeConfigDir } from "../../shared"
import { getOpenCodeConfigDir } from "../../shared/opencode-config-dir"
import type { CommandDefinition } from "../claude-code-command-loader/types"
import type { LoadedSkill } from "./types"
import { skillsToCommandDefinitionRecord } from "./skill-definition-record"
import { deduplicateSkillsByName } from "./skill-deduplication"
import { loadSkillsFromDir } from "./skill-directory-loader"
export async function loadUserSkills(): Promise<Record<string, CommandDefinition>> {
const userSkillsDir = join(getClaudeConfigDir(), "skills")
const skills = await loadSkillsFromDir({ skillsDir: userSkillsDir, scope: "user" })
return skillsToCommandDefinitionRecord(skills)
}
export async function loadProjectSkills(): Promise<Record<string, CommandDefinition>> {
const projectSkillsDir = join(process.cwd(), ".claude", "skills")
const skills = await loadSkillsFromDir({ skillsDir: projectSkillsDir, scope: "project" })
return skillsToCommandDefinitionRecord(skills)
}
export async function loadOpencodeGlobalSkills(): Promise<Record<string, CommandDefinition>> {
const configDir = getOpenCodeConfigDir({ binary: "opencode" })
const opencodeSkillsDir = join(configDir, "skills")
const skills = await loadSkillsFromDir({ skillsDir: opencodeSkillsDir, scope: "opencode" })
return skillsToCommandDefinitionRecord(skills)
}
export async function loadOpencodeProjectSkills(): Promise<Record<string, CommandDefinition>> {
const opencodeProjectDir = join(process.cwd(), ".opencode", "skills")
const skills = await loadSkillsFromDir({ skillsDir: opencodeProjectDir, scope: "opencode-project" })
return skillsToCommandDefinitionRecord(skills)
}
export interface DiscoverSkillsOptions {
includeClaudeCodePaths?: boolean
}
export async function discoverAllSkills(): Promise<LoadedSkill[]> {
const [opencodeProjectSkills, opencodeGlobalSkills, projectSkills, userSkills] = await Promise.all([
discoverOpencodeProjectSkills(),
discoverOpencodeGlobalSkills(),
discoverProjectClaudeSkills(),
discoverUserClaudeSkills(),
])
// Priority: opencode-project > opencode > project > user
return deduplicateSkillsByName([...opencodeProjectSkills, ...opencodeGlobalSkills, ...projectSkills, ...userSkills])
}
export async function discoverSkills(options: DiscoverSkillsOptions = {}): Promise<LoadedSkill[]> {
const { includeClaudeCodePaths = true } = options
const [opencodeProjectSkills, opencodeGlobalSkills] = await Promise.all([
discoverOpencodeProjectSkills(),
discoverOpencodeGlobalSkills(),
])
if (!includeClaudeCodePaths) {
// Priority: opencode-project > opencode
return deduplicateSkillsByName([...opencodeProjectSkills, ...opencodeGlobalSkills])
}
const [projectSkills, userSkills] = await Promise.all([
discoverProjectClaudeSkills(),
discoverUserClaudeSkills(),
])
// Priority: opencode-project > opencode > project > user
return deduplicateSkillsByName([...opencodeProjectSkills, ...opencodeGlobalSkills, ...projectSkills, ...userSkills])
}
export async function getSkillByName(name: string, options: DiscoverSkillsOptions = {}): Promise<LoadedSkill | undefined> {
const skills = await discoverSkills(options)
return skills.find(s => s.name === name)
}
export async function discoverUserClaudeSkills(): Promise<LoadedSkill[]> {
const userSkillsDir = join(getClaudeConfigDir(), "skills")
return loadSkillsFromDir({ skillsDir: userSkillsDir, scope: "user" })
}
export async function discoverProjectClaudeSkills(): Promise<LoadedSkill[]> {
const projectSkillsDir = join(process.cwd(), ".claude", "skills")
return loadSkillsFromDir({ skillsDir: projectSkillsDir, scope: "project" })
}
export async function discoverOpencodeGlobalSkills(): Promise<LoadedSkill[]> {
const configDir = getOpenCodeConfigDir({ binary: "opencode" })
const opencodeSkillsDir = join(configDir, "skills")
return loadSkillsFromDir({ skillsDir: opencodeSkillsDir, scope: "opencode" })
}
export async function discoverOpencodeProjectSkills(): Promise<LoadedSkill[]> {
const opencodeProjectDir = join(process.cwd(), ".opencode", "skills")
return loadSkillsFromDir({ skillsDir: opencodeProjectDir, scope: "opencode-project" })
}