From a400adae97b0819d44d151d04de08c269d266ff7 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Thu, 12 Mar 2026 18:53:44 +0900 Subject: [PATCH] feat(skill): render skills as slash commands in available items list Skills now appear as items with / prefix (e.g., /review-work) instead of items, making them discoverable alongside regular slash commands in the skill tool description. --- src/tools/skill/tools.test.ts | 33 ++++++++++++++++++--------------- src/tools/skill/tools.ts | 11 ++++++----- 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/src/tools/skill/tools.test.ts b/src/tools/skill/tools.test.ts index e64a20fb4..e19b72c54 100644 --- a/src/tools/skill/tools.test.ts +++ b/src/tools/skill/tools.test.ts @@ -384,7 +384,7 @@ describe("skill tool - ordering and priority", () => { } } - it("lists skills before commands in available_items", () => { + it("shows skills as command items with slash prefix in available_items", () => { //#given: mix of skills and commands const skills = [ createMockSkillWithScope("builtin-skill", "builtin"), @@ -398,16 +398,17 @@ describe("skill tool - ordering and priority", () => { //#when: creating tool with both const tool = createSkillTool({ skills, commands }) - //#then: skills should appear before commands + //#then: skills should appear as items with / prefix, listed before regular commands const desc = tool.description - const skillIndex = desc.indexOf("") - const commandIndex = desc.indexOf("") - expect(skillIndex).toBeGreaterThan(0) - expect(commandIndex).toBeGreaterThan(0) - expect(skillIndex).toBeLessThan(commandIndex) + expect(desc).toContain("/builtin-skill") + expect(desc).toContain("/project-skill") + expect(desc).not.toContain("") + const skillCmdIndex = desc.indexOf("/project-skill") + const regularCmdIndex = desc.indexOf("/project-cmd") + expect(skillCmdIndex).toBeLessThan(regularCmdIndex) }) - it("sorts skills by priority: project > user > opencode > builtin", () => { + it("sorts skill-commands by priority: project > user > opencode > builtin", () => { //#given: skills in random order const skills = [ createMockSkillWithScope("builtin-skill", "builtin"), @@ -421,10 +422,10 @@ describe("skill tool - ordering and priority", () => { //#then: should be sorted by priority const desc = tool.description - const projectIndex = desc.indexOf("project-skill") - const userIndex = desc.indexOf("user-skill") - const opencodeIndex = desc.indexOf("opencode-skill") - const builtinIndex = desc.indexOf("builtin-skill") + const projectIndex = desc.indexOf("/project-skill") + const userIndex = desc.indexOf("/user-skill") + const opencodeIndex = desc.indexOf("/opencode-skill") + const builtinIndex = desc.indexOf("/builtin-skill") expect(projectIndex).toBeLessThan(userIndex) expect(userIndex).toBeLessThan(opencodeIndex) @@ -468,7 +469,7 @@ describe("skill tool - ordering and priority", () => { expect(tool.description).toContain("Skills listed before commands") }) - it("uses wrapper with unified format", () => { + it("uses wrapper with unified command format", () => { //#given: mix of skills and commands const skills = [createMockSkillWithScope("test-skill", "project")] const commands = [createMockCommand("test-cmd", "project")] @@ -476,10 +477,12 @@ describe("skill tool - ordering and priority", () => { //#when: creating tool const tool = createSkillTool({ skills, commands }) - //#then: should use unified wrapper + //#then: should use unified wrapper with all items as commands expect(tool.description).toContain("") expect(tool.description).toContain("") - expect(tool.description).toContain("") + expect(tool.description).not.toContain("") expect(tool.description).toContain("") + expect(tool.description).toContain("/test-skill") + expect(tool.description).toContain("/test-cmd") }) }) diff --git a/src/tools/skill/tools.ts b/src/tools/skill/tools.ts index 4bdfb42a0..6836b9e59 100644 --- a/src/tools/skill/tools.ts +++ b/src/tools/skill/tools.ts @@ -45,23 +45,24 @@ function formatCombinedDescription(skills: SkillInfo[], commands: CommandInfo[]) const allItems: string[] = [] - // Sort and add skills first (skills before commands) + // Skills rendered as command items (skills are also slash-invocable) if (skills.length > 0) { const sortedSkills = [...skills].sort((a, b) => { const priorityA = scopePriority[a.scope] || 0 const priorityB = scopePriority[b.scope] || 0 - return priorityB - priorityA // Higher priority first + return priorityB - priorityA }) sortedSkills.forEach(skill => { const parts = [ - " ", - ` ${skill.name}`, + " ", + ` /${skill.name}`, ` ${skill.description}`, + ` ${skill.scope}`, ] if (skill.compatibility) { parts.push(` ${skill.compatibility}`) } - parts.push(" ") + parts.push(" ") allItems.push(parts.join("\n")) }) }