From fe11ba294c4fc53abead51d07ebe1b238ea2b628 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Mon, 5 Jan 2026 14:03:17 +0900 Subject: [PATCH] perf(startup): parallelize command and skill loading in config-handler - Add async versions of skill loader functions (loadUserSkillsAsync, loadProjectSkillsAsync, loadOpencodeGlobalSkillsAsync, loadOpencodeProjectSkillsAsync) - Use Promise.all to load 8 loaders concurrently instead of sequentially - Improves startup performance by eliminating serial I/O bottlenecks Generated with assistance of OhMyOpenCode --- src/features/opencode-skill-loader/loader.ts | 36 +++++++++++++ src/plugin-handlers/config-handler.ts | 55 +++++++++++--------- 2 files changed, 67 insertions(+), 24 deletions(-) diff --git a/src/features/opencode-skill-loader/loader.ts b/src/features/opencode-skill-loader/loader.ts index 9e2eb64..be9b756 100644 --- a/src/features/opencode-skill-loader/loader.ts +++ b/src/features/opencode-skill-loader/loader.ts @@ -340,6 +340,42 @@ export function loadOpencodeProjectSkills(): Record { return skillsToRecord(skills) } +/** + * Async version of loadUserSkills + */ +export async function loadUserSkillsAsync(): Promise> { + const userSkillsDir = join(getClaudeConfigDir(), "skills") + const skills = await loadSkillsFromDirAsync(userSkillsDir, "user") + return skillsToRecord(skills) +} + +/** + * Async version of loadProjectSkills + */ +export async function loadProjectSkillsAsync(): Promise> { + const projectSkillsDir = join(process.cwd(), ".claude", "skills") + const skills = await loadSkillsFromDirAsync(projectSkillsDir, "project") + return skillsToRecord(skills) +} + +/** + * Async version of loadOpencodeGlobalSkills + */ +export async function loadOpencodeGlobalSkillsAsync(): Promise> { + const opencodeSkillsDir = join(homedir(), ".config", "opencode", "skill") + const skills = await loadSkillsFromDirAsync(opencodeSkillsDir, "opencode") + return skillsToRecord(skills) +} + +/** + * Async version of loadOpencodeProjectSkills + */ +export async function loadOpencodeProjectSkillsAsync(): Promise> { + const opencodeProjectDir = join(process.cwd(), ".opencode", "skill") + const skills = await loadSkillsFromDirAsync(opencodeProjectDir, "opencode-project") + return skillsToRecord(skills) +} + /** * Discover all skills from all sources with priority ordering. * Priority order: opencode-project > project > opencode > user diff --git a/src/plugin-handlers/config-handler.ts b/src/plugin-handlers/config-handler.ts index 8ff2b0a..3c9dd2b 100644 --- a/src/plugin-handlers/config-handler.ts +++ b/src/plugin-handlers/config-handler.ts @@ -1,16 +1,16 @@ import { createBuiltinAgents } from "../agents"; import { - loadUserCommands, - loadProjectCommands, - loadOpencodeGlobalCommands, - loadOpencodeProjectCommands, + loadUserCommandsAsync, + loadProjectCommandsAsync, + loadOpencodeGlobalCommandsAsync, + loadOpencodeProjectCommandsAsync, } from "../features/claude-code-command-loader"; import { loadBuiltinCommands } from "../features/builtin-commands"; import { - loadUserSkills, - loadProjectSkills, - loadOpencodeGlobalSkills, - loadOpencodeProjectSkills, + loadUserSkillsAsync, + loadProjectSkillsAsync, + loadOpencodeGlobalSkillsAsync, + loadOpencodeProjectSkillsAsync, } from "../features/opencode-skill-loader"; import { loadUserAgents, @@ -282,24 +282,31 @@ export function createConfigHandler(deps: ConfigHandlerDeps) { }; const builtinCommands = loadBuiltinCommands(pluginConfig.disabled_commands); - const userCommands = (pluginConfig.claude_code?.commands ?? true) - ? loadUserCommands() - : {}; - const opencodeGlobalCommands = loadOpencodeGlobalCommands(); const systemCommands = (config.command as Record) ?? {}; - const projectCommands = (pluginConfig.claude_code?.commands ?? true) - ? loadProjectCommands() - : {}; - const opencodeProjectCommands = loadOpencodeProjectCommands(); - const userSkills = (pluginConfig.claude_code?.skills ?? true) - ? loadUserSkills() - : {}; - const projectSkills = (pluginConfig.claude_code?.skills ?? true) - ? loadProjectSkills() - : {}; - const opencodeGlobalSkills = loadOpencodeGlobalSkills(); - const opencodeProjectSkills = loadOpencodeProjectSkills(); + // Parallel loading of all commands and skills for faster startup + const includeClaudeCommands = pluginConfig.claude_code?.commands ?? true; + const includeClaudeSkills = pluginConfig.claude_code?.skills ?? true; + + const [ + userCommands, + projectCommands, + opencodeGlobalCommands, + opencodeProjectCommands, + userSkills, + projectSkills, + opencodeGlobalSkills, + opencodeProjectSkills, + ] = await Promise.all([ + includeClaudeCommands ? loadUserCommandsAsync() : Promise.resolve({}), + includeClaudeCommands ? loadProjectCommandsAsync() : Promise.resolve({}), + loadOpencodeGlobalCommandsAsync(), + loadOpencodeProjectCommandsAsync(), + includeClaudeSkills ? loadUserSkillsAsync() : Promise.resolve({}), + includeClaudeSkills ? loadProjectSkillsAsync() : Promise.resolve({}), + loadOpencodeGlobalSkillsAsync(), + loadOpencodeProjectSkillsAsync(), + ]); config.command = { ...builtinCommands,