From 6bbe69a72a7c06537fa20368380c379484f292a5 Mon Sep 17 00:00:00 2001 From: Momentum96 Date: Mon, 12 Jan 2026 17:27:54 +0900 Subject: [PATCH 1/2] fix(skill-loader): implement eager loading to resolve empty slash commands --- src/features/opencode-skill-loader/loader.ts | 22 +++++++------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/src/features/opencode-skill-loader/loader.ts b/src/features/opencode-skill-loader/loader.ts index 2d59f147..5d6561da 100644 --- a/src/features/opencode-skill-loader/loader.ts +++ b/src/features/opencode-skill-loader/loader.ts @@ -63,7 +63,7 @@ async function loadSkillFromPath( ): Promise { try { const content = await fs.readFile(skillPath, "utf-8") - const { data } = parseFrontmatter(content) + const { data, body } = parseFrontmatter(content) const frontmatterMcp = parseSkillMcpConfigFromFrontmatter(content) const mcpJsonMcp = await loadMcpJsonFromDir(resolvedPath) const mcpConfig = mcpJsonMcp || frontmatterMcp @@ -73,14 +73,7 @@ async function loadSkillFromPath( const isOpencodeSource = scope === "opencode" || scope === "opencode-project" const formattedDescription = `(${scope} - Skill) ${originalDescription}` - const lazyContent: LazyContentLoader = { - loaded: false, - content: undefined, - load: async () => { - if (!lazyContent.loaded) { - const fileContent = await fs.readFile(skillPath, "utf-8") - const { body } = parseFrontmatter(fileContent) - lazyContent.content = ` + const templateContent = ` Base directory for this skill: ${resolvedPath}/ File references (@path) in this skill are relative to this directory. @@ -90,16 +83,17 @@ ${body.trim()} $ARGUMENTS ` - lazyContent.loaded = true - } - return lazyContent.content! - }, + + const lazyContent: LazyContentLoader = { + loaded: true, + content: templateContent, + load: async () => templateContent, } const definition: CommandDefinition = { name: skillName, description: formattedDescription, - template: "", + template: templateContent, model: sanitizeModelField(data.model, isOpencodeSource ? "opencode" : "claude-code"), agent: data.agent, subtask: data.subtask, From 79bd75b3dbf5f62d8b20ff8d85a9919cc6f9a7ac Mon Sep 17 00:00:00 2001 From: Kenny Date: Mon, 12 Jan 2026 22:46:28 -0500 Subject: [PATCH 2/2] refactor(skill-loader): eager loading with atomic file reads MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Extract body during initial parseFrontmatter call - Rename lazyContent → eagerLoader with rationale comment - Eliminates redundant file read and race condition --- src/features/opencode-skill-loader/loader.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/features/opencode-skill-loader/loader.ts b/src/features/opencode-skill-loader/loader.ts index 5d6561da..4bff1ca1 100644 --- a/src/features/opencode-skill-loader/loader.ts +++ b/src/features/opencode-skill-loader/loader.ts @@ -84,7 +84,10 @@ ${body.trim()} $ARGUMENTS ` - const lazyContent: LazyContentLoader = { + // RATIONALE: We read the file eagerly to ensure atomic consistency between + // metadata and body. We maintain the LazyContentLoader interface for + // compatibility, but the state is effectively eager. + const eagerLoader: LazyContentLoader = { loaded: true, content: templateContent, load: async () => templateContent, @@ -111,7 +114,7 @@ $ARGUMENTS metadata: data.metadata, allowedTools: parseAllowedTools(data["allowed-tools"]), mcpConfig, - lazyContent, + lazyContent: eagerLoader, } } catch { return null