feat(skill): render skills as slash commands in available items list
Skills now appear as <command> items with / prefix (e.g., /review-work) instead of <skill> items, making them discoverable alongside regular slash commands in the skill tool description.
This commit is contained in:
@@ -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
|
//#given: mix of skills and commands
|
||||||
const skills = [
|
const skills = [
|
||||||
createMockSkillWithScope("builtin-skill", "builtin"),
|
createMockSkillWithScope("builtin-skill", "builtin"),
|
||||||
@@ -398,16 +398,17 @@ describe("skill tool - ordering and priority", () => {
|
|||||||
//#when: creating tool with both
|
//#when: creating tool with both
|
||||||
const tool = createSkillTool({ skills, commands })
|
const tool = createSkillTool({ skills, commands })
|
||||||
|
|
||||||
//#then: skills should appear before commands
|
//#then: skills should appear as <command> items with / prefix, listed before regular commands
|
||||||
const desc = tool.description
|
const desc = tool.description
|
||||||
const skillIndex = desc.indexOf("<skill>")
|
expect(desc).toContain("<name>/builtin-skill</name>")
|
||||||
const commandIndex = desc.indexOf("<command>")
|
expect(desc).toContain("<name>/project-skill</name>")
|
||||||
expect(skillIndex).toBeGreaterThan(0)
|
expect(desc).not.toContain("<skill>")
|
||||||
expect(commandIndex).toBeGreaterThan(0)
|
const skillCmdIndex = desc.indexOf("/project-skill")
|
||||||
expect(skillIndex).toBeLessThan(commandIndex)
|
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
|
//#given: skills in random order
|
||||||
const skills = [
|
const skills = [
|
||||||
createMockSkillWithScope("builtin-skill", "builtin"),
|
createMockSkillWithScope("builtin-skill", "builtin"),
|
||||||
@@ -421,10 +422,10 @@ describe("skill tool - ordering and priority", () => {
|
|||||||
|
|
||||||
//#then: should be sorted by priority
|
//#then: should be sorted by priority
|
||||||
const desc = tool.description
|
const desc = tool.description
|
||||||
const projectIndex = desc.indexOf("project-skill")
|
const projectIndex = desc.indexOf("/project-skill")
|
||||||
const userIndex = desc.indexOf("user-skill")
|
const userIndex = desc.indexOf("/user-skill")
|
||||||
const opencodeIndex = desc.indexOf("opencode-skill")
|
const opencodeIndex = desc.indexOf("/opencode-skill")
|
||||||
const builtinIndex = desc.indexOf("builtin-skill")
|
const builtinIndex = desc.indexOf("/builtin-skill")
|
||||||
|
|
||||||
expect(projectIndex).toBeLessThan(userIndex)
|
expect(projectIndex).toBeLessThan(userIndex)
|
||||||
expect(userIndex).toBeLessThan(opencodeIndex)
|
expect(userIndex).toBeLessThan(opencodeIndex)
|
||||||
@@ -468,7 +469,7 @@ describe("skill tool - ordering and priority", () => {
|
|||||||
expect(tool.description).toContain("Skills listed before commands")
|
expect(tool.description).toContain("Skills listed before commands")
|
||||||
})
|
})
|
||||||
|
|
||||||
it("uses <available_items> wrapper with unified format", () => {
|
it("uses <available_items> wrapper with unified command format", () => {
|
||||||
//#given: mix of skills and commands
|
//#given: mix of skills and commands
|
||||||
const skills = [createMockSkillWithScope("test-skill", "project")]
|
const skills = [createMockSkillWithScope("test-skill", "project")]
|
||||||
const commands = [createMockCommand("test-cmd", "project")]
|
const commands = [createMockCommand("test-cmd", "project")]
|
||||||
@@ -476,10 +477,12 @@ describe("skill tool - ordering and priority", () => {
|
|||||||
//#when: creating tool
|
//#when: creating tool
|
||||||
const tool = createSkillTool({ skills, commands })
|
const tool = createSkillTool({ skills, commands })
|
||||||
|
|
||||||
//#then: should use unified wrapper
|
//#then: should use unified wrapper with all items as commands
|
||||||
expect(tool.description).toContain("<available_items>")
|
expect(tool.description).toContain("<available_items>")
|
||||||
expect(tool.description).toContain("</available_items>")
|
expect(tool.description).toContain("</available_items>")
|
||||||
expect(tool.description).toContain("<skill>")
|
expect(tool.description).not.toContain("<skill>")
|
||||||
expect(tool.description).toContain("<command>")
|
expect(tool.description).toContain("<command>")
|
||||||
|
expect(tool.description).toContain("/test-skill")
|
||||||
|
expect(tool.description).toContain("/test-cmd")
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -45,23 +45,24 @@ function formatCombinedDescription(skills: SkillInfo[], commands: CommandInfo[])
|
|||||||
|
|
||||||
const allItems: string[] = []
|
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) {
|
if (skills.length > 0) {
|
||||||
const sortedSkills = [...skills].sort((a, b) => {
|
const sortedSkills = [...skills].sort((a, b) => {
|
||||||
const priorityA = scopePriority[a.scope] || 0
|
const priorityA = scopePriority[a.scope] || 0
|
||||||
const priorityB = scopePriority[b.scope] || 0
|
const priorityB = scopePriority[b.scope] || 0
|
||||||
return priorityB - priorityA // Higher priority first
|
return priorityB - priorityA
|
||||||
})
|
})
|
||||||
sortedSkills.forEach(skill => {
|
sortedSkills.forEach(skill => {
|
||||||
const parts = [
|
const parts = [
|
||||||
" <skill>",
|
" <command>",
|
||||||
` <name>${skill.name}</name>`,
|
` <name>/${skill.name}</name>`,
|
||||||
` <description>${skill.description}</description>`,
|
` <description>${skill.description}</description>`,
|
||||||
|
` <scope>${skill.scope}</scope>`,
|
||||||
]
|
]
|
||||||
if (skill.compatibility) {
|
if (skill.compatibility) {
|
||||||
parts.push(` <compatibility>${skill.compatibility}</compatibility>`)
|
parts.push(` <compatibility>${skill.compatibility}</compatibility>`)
|
||||||
}
|
}
|
||||||
parts.push(" </skill>")
|
parts.push(" </command>")
|
||||||
allItems.push(parts.join("\n"))
|
allItems.push(parts.join("\n"))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user