diff --git a/src/tools/skill/tools.factory.test.ts b/src/tools/skill/tools.factory.test.ts
new file mode 100644
index 000000000..631162dd4
--- /dev/null
+++ b/src/tools/skill/tools.factory.test.ts
@@ -0,0 +1,94 @@
+///
+
+import { afterEach, describe, expect, it, mock } from "bun:test"
+import type { LoadedSkill } from "../../features/opencode-skill-loader/types"
+
+function createMockSkill(name: string): LoadedSkill {
+ return {
+ name,
+ definition: {
+ name,
+ description: `Test skill ${name}`,
+ template: `Test skill template for ${name}`,
+ },
+ scope: "config",
+ }
+}
+
+async function flushMicrotasks(): Promise {
+ await Promise.resolve()
+ await Promise.resolve()
+}
+
+const loadedSkill = createMockSkill("lazy-skill")
+const discoverCommandsSync = mock(() => [])
+const getAllSkills = mock(async () => [loadedSkill])
+const clearSkillCache = mock(() => {})
+
+const skillContentModuleFactory = () => ({
+ clearSkillCache,
+ getAllSkills,
+ extractSkillTemplate: () => loadedSkill.definition.template ?? "",
+ injectGitMasterConfig: (body: string) => body,
+})
+const commandDiscoveryModuleFactory = () => ({
+ discoverCommandsSync,
+})
+
+mock.module("../../features/opencode-skill-loader/skill-content", skillContentModuleFactory)
+mock.module("../../features/opencode-skill-loader/skill-content.ts", skillContentModuleFactory)
+mock.module("../slashcommand/command-discovery", commandDiscoveryModuleFactory)
+mock.module("../slashcommand/command-discovery.ts", commandDiscoveryModuleFactory)
+
+const { createSkillTool } = await import("./tools")
+
+afterEach(async () => {
+ await flushMicrotasks()
+})
+
+describe("createSkillTool", () => {
+ it("delays command discovery until the description getter is accessed", async () => {
+ // given
+ const baselineDiscoverCommandsSyncCalls = discoverCommandsSync.mock.calls.length
+
+ // when
+ const skillTool = createSkillTool({})
+
+ // then
+ expect(discoverCommandsSync.mock.calls.length).toBe(baselineDiscoverCommandsSyncCalls)
+
+ void skillTool.description
+ await flushMicrotasks()
+
+ expect(discoverCommandsSync.mock.calls.length).toBe(baselineDiscoverCommandsSyncCalls + 1)
+ })
+
+ it("delays skill loading until execute is invoked", async () => {
+ // given
+ const baselineGetAllSkillsCalls = getAllSkills.mock.calls.length
+
+ // when
+ const skillTool = createSkillTool({})
+
+ // then
+ expect(getAllSkills.mock.calls.length).toBe(baselineGetAllSkillsCalls)
+
+ await skillTool.execute({ name: "lazy-skill" })
+
+ expect(getAllSkills.mock.calls.length).toBe(baselineGetAllSkillsCalls + 1)
+ })
+
+ it("does not clear the shared skill cache during description or execute refresh", async () => {
+ // given
+ const baselineClearSkillCacheCalls = clearSkillCache.mock.calls.length
+
+ // when
+ const skillTool = createSkillTool({})
+ void skillTool.description
+ await flushMicrotasks()
+ await skillTool.execute({ name: "lazy-skill" })
+
+ // then
+ expect(clearSkillCache.mock.calls.length).toBe(baselineClearSkillCacheCalls)
+ })
+})
diff --git a/src/tools/skill/tools.ts b/src/tools/skill/tools.ts
index 0fada5607..680fe638e 100644
--- a/src/tools/skill/tools.ts
+++ b/src/tools/skill/tools.ts
@@ -4,7 +4,7 @@ import type { ToolContext } from "@opencode-ai/plugin/tool"
import { TOOL_DESCRIPTION_PREFIX } from "./constants"
import type { SkillArgs, SkillLoadOptions } from "./types"
import type { LoadedSkill } from "../../features/opencode-skill-loader"
-import { getAllSkills, clearSkillCache } from "../../features/opencode-skill-loader/skill-content"
+import { getAllSkills } from "../../features/opencode-skill-loader/skill-content"
import { injectGitMasterConfig } from "../../features/opencode-skill-loader/skill-content"
import { discoverCommandsSync } from "../slashcommand/command-discovery"
import type { CommandInfo } from "../slashcommand/types"
@@ -28,7 +28,6 @@ export function createSkillTool(options: SkillLoadOptions = {}): ToolDefinition
let cachedDescription: string | null = null
const getSkills = async (): Promise => {
- clearSkillCache()
const discovered = await getAllSkills({
disabledSkills: options?.disabledSkills,
browserProvider: options?.browserProvider,
@@ -92,8 +91,6 @@ export function createSkillTool(options: SkillLoadOptions = {}): ToolDefinition
}
} else if (options.commands !== undefined) {
cachedDescription = formatCombinedDescription([], options.commands)
- } else {
- void buildDescription()
}
return tool({