feat: add skill metadata and discovery functions to opencode-skill-loader

- 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)
This commit is contained in:
YeonGyu-Kim
2025-12-30 11:22:51 +09:00
parent 2c778d9352
commit 1f1fefe8b7
2 changed files with 55 additions and 0 deletions

View File

@@ -16,6 +16,11 @@ import type { SkillScope, SkillMetadata, LoadedSkill } from "./types"
* @param defaultName - Fallback name if not specified in frontmatter * @param defaultName - Fallback name if not specified in frontmatter
* @param scope - Source scope for priority ordering * @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( function loadSkillFromPath(
skillPath: string, skillPath: string,
resolvedPath: string, resolvedPath: string,
@@ -58,6 +63,10 @@ $ARGUMENTS
resolvedPath, resolvedPath,
definition, definition,
scope, scope,
license: data.license,
compatibility: data.compatibility,
metadata: data.metadata,
allowedTools: parseAllowedTools(data["allowed-tools"]),
} }
} catch { } catch {
return null return null
@@ -177,3 +186,41 @@ export function discoverAllSkills(): LoadedSkill[] {
return [...opencodeProjectSkills, ...projectSkills, ...opencodeGlobalSkills, ...userSkills] 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)
}

View File

@@ -9,6 +9,10 @@ export interface SkillMetadata {
"argument-hint"?: string "argument-hint"?: string
agent?: string agent?: string
subtask?: boolean subtask?: boolean
license?: string
compatibility?: string
metadata?: Record<string, string>
"allowed-tools"?: string
} }
export interface LoadedSkill { export interface LoadedSkill {
@@ -17,4 +21,8 @@ export interface LoadedSkill {
resolvedPath: string resolvedPath: string
definition: CommandDefinition definition: CommandDefinition
scope: SkillScope scope: SkillScope
license?: string
compatibility?: string
metadata?: Record<string, string>
allowedTools?: string[]
} }