2026-02-08 16:21:19 +09:00
|
|
|
import { join } from "path"
|
2026-02-10 11:16:55 +09:00
|
|
|
import { getClaudeConfigDir } from "../../shared/claude-config-dir"
|
2026-01-21 13:12:27 +01:00
|
|
|
import { getOpenCodeConfigDir } from "../../shared/opencode-config-dir"
|
2025-12-30 10:42:05 +09:00
|
|
|
import type { CommandDefinition } from "../claude-code-command-loader/types"
|
2026-02-08 16:21:19 +09:00
|
|
|
import type { LoadedSkill } from "./types"
|
|
|
|
|
import { skillsToCommandDefinitionRecord } from "./skill-definition-record"
|
|
|
|
|
import { deduplicateSkillsByName } from "./skill-deduplication"
|
|
|
|
|
import { loadSkillsFromDir } from "./skill-directory-loader"
|
2025-12-30 10:42:05 +09:00
|
|
|
|
2026-01-05 14:18:42 +09:00
|
|
|
export async function loadUserSkills(): Promise<Record<string, CommandDefinition>> {
|
2025-12-30 10:42:05 +09:00
|
|
|
const userSkillsDir = join(getClaudeConfigDir(), "skills")
|
2026-02-08 16:21:19 +09:00
|
|
|
const skills = await loadSkillsFromDir({ skillsDir: userSkillsDir, scope: "user" })
|
|
|
|
|
return skillsToCommandDefinitionRecord(skills)
|
2025-12-30 10:42:05 +09:00
|
|
|
}
|
|
|
|
|
|
2026-02-13 10:49:15 +08:00
|
|
|
export async function loadProjectSkills(directory?: string): Promise<Record<string, CommandDefinition>> {
|
|
|
|
|
const projectSkillsDir = join(directory ?? process.cwd(), ".claude", "skills")
|
2026-02-08 16:21:19 +09:00
|
|
|
const skills = await loadSkillsFromDir({ skillsDir: projectSkillsDir, scope: "project" })
|
|
|
|
|
return skillsToCommandDefinitionRecord(skills)
|
2025-12-30 10:42:05 +09:00
|
|
|
}
|
|
|
|
|
|
2026-01-05 14:18:42 +09:00
|
|
|
export async function loadOpencodeGlobalSkills(): Promise<Record<string, CommandDefinition>> {
|
2026-01-21 13:12:27 +01:00
|
|
|
const configDir = getOpenCodeConfigDir({ binary: "opencode" })
|
|
|
|
|
const opencodeSkillsDir = join(configDir, "skills")
|
2026-02-08 16:21:19 +09:00
|
|
|
const skills = await loadSkillsFromDir({ skillsDir: opencodeSkillsDir, scope: "opencode" })
|
|
|
|
|
return skillsToCommandDefinitionRecord(skills)
|
2025-12-30 10:42:05 +09:00
|
|
|
}
|
|
|
|
|
|
2026-02-13 10:49:15 +08:00
|
|
|
export async function loadOpencodeProjectSkills(directory?: string): Promise<Record<string, CommandDefinition>> {
|
|
|
|
|
const opencodeProjectDir = join(directory ?? process.cwd(), ".opencode", "skills")
|
2026-02-08 16:21:19 +09:00
|
|
|
const skills = await loadSkillsFromDir({ skillsDir: opencodeProjectDir, scope: "opencode-project" })
|
|
|
|
|
return skillsToCommandDefinitionRecord(skills)
|
2026-01-05 14:03:17 +09:00
|
|
|
}
|
|
|
|
|
|
2026-01-05 14:18:42 +09:00
|
|
|
export interface DiscoverSkillsOptions {
|
|
|
|
|
includeClaudeCodePaths?: boolean
|
2026-02-13 10:49:15 +08:00
|
|
|
directory?: string
|
2026-01-05 14:03:17 +09:00
|
|
|
}
|
|
|
|
|
|
2026-02-13 10:49:15 +08:00
|
|
|
export async function discoverAllSkills(directory?: string): Promise<LoadedSkill[]> {
|
2026-02-05 02:36:43 +09:00
|
|
|
const [opencodeProjectSkills, opencodeGlobalSkills, projectSkills, userSkills] = await Promise.all([
|
2026-02-13 10:49:15 +08:00
|
|
|
discoverOpencodeProjectSkills(directory),
|
2026-01-05 14:18:42 +09:00
|
|
|
discoverOpencodeGlobalSkills(),
|
2026-02-13 10:49:15 +08:00
|
|
|
discoverProjectClaudeSkills(directory),
|
2026-01-05 14:18:42 +09:00
|
|
|
discoverUserClaudeSkills(),
|
|
|
|
|
])
|
2025-12-30 10:42:05 +09:00
|
|
|
|
2026-02-05 02:33:56 +09:00
|
|
|
// Priority: opencode-project > opencode > project > user
|
2026-02-08 16:21:19 +09:00
|
|
|
return deduplicateSkillsByName([...opencodeProjectSkills, ...opencodeGlobalSkills, ...projectSkills, ...userSkills])
|
2025-12-30 10:42:05 +09:00
|
|
|
}
|
2025-12-30 11:22:51 +09:00
|
|
|
|
2026-01-05 14:18:42 +09:00
|
|
|
export async function discoverSkills(options: DiscoverSkillsOptions = {}): Promise<LoadedSkill[]> {
|
2026-02-13 10:49:15 +08:00
|
|
|
const { includeClaudeCodePaths = true, directory } = options
|
2025-12-30 11:22:51 +09:00
|
|
|
|
2026-01-05 14:18:42 +09:00
|
|
|
const [opencodeProjectSkills, opencodeGlobalSkills] = await Promise.all([
|
2026-02-13 10:49:15 +08:00
|
|
|
discoverOpencodeProjectSkills(directory),
|
2026-01-05 14:18:42 +09:00
|
|
|
discoverOpencodeGlobalSkills(),
|
|
|
|
|
])
|
2025-12-30 11:22:51 +09:00
|
|
|
|
|
|
|
|
if (!includeClaudeCodePaths) {
|
2026-02-05 01:54:33 +09:00
|
|
|
// Priority: opencode-project > opencode
|
2026-02-08 16:21:19 +09:00
|
|
|
return deduplicateSkillsByName([...opencodeProjectSkills, ...opencodeGlobalSkills])
|
2025-12-30 11:22:51 +09:00
|
|
|
}
|
|
|
|
|
|
2026-01-05 14:18:42 +09:00
|
|
|
const [projectSkills, userSkills] = await Promise.all([
|
2026-02-13 10:49:15 +08:00
|
|
|
discoverProjectClaudeSkills(directory),
|
2026-01-05 14:18:42 +09:00
|
|
|
discoverUserClaudeSkills(),
|
|
|
|
|
])
|
2025-12-30 11:22:51 +09:00
|
|
|
|
2026-02-05 02:33:56 +09:00
|
|
|
// Priority: opencode-project > opencode > project > user
|
2026-02-08 16:21:19 +09:00
|
|
|
return deduplicateSkillsByName([...opencodeProjectSkills, ...opencodeGlobalSkills, ...projectSkills, ...userSkills])
|
2025-12-30 11:22:51 +09:00
|
|
|
}
|
|
|
|
|
|
2026-01-05 14:18:42 +09:00
|
|
|
export async function getSkillByName(name: string, options: DiscoverSkillsOptions = {}): Promise<LoadedSkill | undefined> {
|
|
|
|
|
const skills = await discoverSkills(options)
|
2025-12-30 11:22:51 +09:00
|
|
|
return skills.find(s => s.name === name)
|
|
|
|
|
}
|
2025-12-30 15:15:43 +09:00
|
|
|
|
2026-01-05 14:18:42 +09:00
|
|
|
export async function discoverUserClaudeSkills(): Promise<LoadedSkill[]> {
|
2025-12-30 15:15:43 +09:00
|
|
|
const userSkillsDir = join(getClaudeConfigDir(), "skills")
|
2026-02-08 16:21:19 +09:00
|
|
|
return loadSkillsFromDir({ skillsDir: userSkillsDir, scope: "user" })
|
2025-12-30 15:15:43 +09:00
|
|
|
}
|
|
|
|
|
|
2026-02-13 10:49:15 +08:00
|
|
|
export async function discoverProjectClaudeSkills(directory?: string): Promise<LoadedSkill[]> {
|
|
|
|
|
const projectSkillsDir = join(directory ?? process.cwd(), ".claude", "skills")
|
2026-02-08 16:21:19 +09:00
|
|
|
return loadSkillsFromDir({ skillsDir: projectSkillsDir, scope: "project" })
|
2025-12-30 15:15:43 +09:00
|
|
|
}
|
|
|
|
|
|
2026-01-05 14:18:42 +09:00
|
|
|
export async function discoverOpencodeGlobalSkills(): Promise<LoadedSkill[]> {
|
2026-01-21 13:12:27 +01:00
|
|
|
const configDir = getOpenCodeConfigDir({ binary: "opencode" })
|
|
|
|
|
const opencodeSkillsDir = join(configDir, "skills")
|
2026-02-08 16:21:19 +09:00
|
|
|
return loadSkillsFromDir({ skillsDir: opencodeSkillsDir, scope: "opencode" })
|
2025-12-30 15:15:43 +09:00
|
|
|
}
|
|
|
|
|
|
2026-02-13 10:49:15 +08:00
|
|
|
export async function discoverOpencodeProjectSkills(directory?: string): Promise<LoadedSkill[]> {
|
|
|
|
|
const opencodeProjectDir = join(directory ?? process.cwd(), ".opencode", "skills")
|
2026-02-08 16:21:19 +09:00
|
|
|
return loadSkillsFromDir({ skillsDir: opencodeProjectDir, scope: "opencode-project" })
|
2025-12-30 15:15:43 +09:00
|
|
|
}
|