From 1f1fefe8b77912461dcde343fceaf877027c59a2 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Tue, 30 Dec 2025 11:22:51 +0900 Subject: [PATCH] feat: add skill metadata and discovery functions to opencode-skill-loader MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add license, compatibility, metadata, and allowed-tools fields to SkillMetadata interface - Add corresponding fields to LoadedSkill interface with proper type transformations - Implement parseAllowedTools() helper for parsing comma/space-separated allowed tools - Add discoverSkills() function with includeClaudeCodePaths option for flexible skill discovery - Add getSkillByName() function for efficient skill lookup by name - Support both OpenCode and Claude Code skill paths based on configuration 🤖 GENERATED WITH ASSISTANCE OF [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode) --- src/features/opencode-skill-loader/loader.ts | 47 ++++++++++++++++++++ src/features/opencode-skill-loader/types.ts | 8 ++++ 2 files changed, 55 insertions(+) diff --git a/src/features/opencode-skill-loader/loader.ts b/src/features/opencode-skill-loader/loader.ts index 9c06614..dd1449a 100644 --- a/src/features/opencode-skill-loader/loader.ts +++ b/src/features/opencode-skill-loader/loader.ts @@ -16,6 +16,11 @@ import type { SkillScope, SkillMetadata, LoadedSkill } from "./types" * @param defaultName - Fallback name if not specified in frontmatter * @param scope - Source scope for priority ordering */ +function parseAllowedTools(allowedTools: string | undefined): string[] | undefined { + if (!allowedTools) return undefined + return allowedTools.split(/\s+/).filter(Boolean) +} + function loadSkillFromPath( skillPath: string, resolvedPath: string, @@ -58,6 +63,10 @@ $ARGUMENTS resolvedPath, definition, scope, + license: data.license, + compatibility: data.compatibility, + metadata: data.metadata, + allowedTools: parseAllowedTools(data["allowed-tools"]), } } catch { return null @@ -177,3 +186,41 @@ export function discoverAllSkills(): LoadedSkill[] { return [...opencodeProjectSkills, ...projectSkills, ...opencodeGlobalSkills, ...userSkills] } + +export interface DiscoverSkillsOptions { + includeClaudeCodePaths?: boolean +} + +/** + * Discover skills with optional filtering. + * When includeClaudeCodePaths is false, only loads from OpenCode paths. + */ +export function discoverSkills(options: DiscoverSkillsOptions = {}): LoadedSkill[] { + const { includeClaudeCodePaths = true } = options + + const opencodeProjectDir = join(process.cwd(), ".opencode", "skill") + const opencodeGlobalDir = join(homedir(), ".config", "opencode", "skill") + + const opencodeProjectSkills = loadSkillsFromDir(opencodeProjectDir, "opencode-project") + const opencodeGlobalSkills = loadSkillsFromDir(opencodeGlobalDir, "opencode") + + if (!includeClaudeCodePaths) { + return [...opencodeProjectSkills, ...opencodeGlobalSkills] + } + + const projectDir = join(process.cwd(), ".claude", "skills") + const userDir = join(getClaudeConfigDir(), "skills") + + const projectSkills = loadSkillsFromDir(projectDir, "project") + const userSkills = loadSkillsFromDir(userDir, "user") + + return [...opencodeProjectSkills, ...projectSkills, ...opencodeGlobalSkills, ...userSkills] +} + +/** + * Get a skill by name from all available sources. + */ +export function getSkillByName(name: string, options: DiscoverSkillsOptions = {}): LoadedSkill | undefined { + const skills = discoverSkills(options) + return skills.find(s => s.name === name) +} diff --git a/src/features/opencode-skill-loader/types.ts b/src/features/opencode-skill-loader/types.ts index 4142bd8..397bb36 100644 --- a/src/features/opencode-skill-loader/types.ts +++ b/src/features/opencode-skill-loader/types.ts @@ -9,6 +9,10 @@ export interface SkillMetadata { "argument-hint"?: string agent?: string subtask?: boolean + license?: string + compatibility?: string + metadata?: Record + "allowed-tools"?: string } export interface LoadedSkill { @@ -17,4 +21,8 @@ export interface LoadedSkill { resolvedPath: string definition: CommandDefinition scope: SkillScope + license?: string + compatibility?: string + metadata?: Record + allowedTools?: string[] }