feat: make skills invokable as slash commands in the TUI

- Add Skill.content() method to load skill template content from SKILL.md files
- Modify Command.list() to include skills as invokable commands
- Add 'skill' boolean property to Command.Info schema
- Update autocomplete to show skills with (Skill) label in slash commands
- Regenerate SDK to include skill property in Command type
This commit is contained in:
Dax Raad
2026-01-30 18:07:14 -05:00
parent 252b2c450d
commit 85126556b8
4 changed files with 27 additions and 1 deletions

View File

@@ -345,8 +345,9 @@ export function Autocomplete(props: {
const results: AutocompleteOption[] = [...command.slashes()]
for (const serverCommand of sync.data.command) {
const label = serverCommand.mcp ? " (MCP)" : serverCommand.skill ? " (Skill)" : ""
results.push({
display: "/" + serverCommand.name + (serverCommand.mcp ? " (MCP)" : ""),
display: "/" + serverCommand.name + label,
description: serverCommand.description,
onSelect: () => {
const newText = "/" + serverCommand.name + " "

View File

@@ -6,6 +6,7 @@ import { Identifier } from "../id/id"
import PROMPT_INITIALIZE from "./template/initialize.txt"
import PROMPT_REVIEW from "./template/review.txt"
import { MCP } from "../mcp"
import { Skill } from "../skill"
export namespace Command {
export const Event = {
@@ -27,6 +28,7 @@ export namespace Command {
agent: z.string().optional(),
model: z.string().optional(),
mcp: z.boolean().optional(),
skill: z.boolean().optional(),
// workaround for zod not supporting async functions natively so we use getters
// https://zod.dev/v4/changelog?id=zfunction
template: z.promise(z.string()).or(z.string()),
@@ -118,6 +120,21 @@ export namespace Command {
}
}
// Add skills as invokable commands
for (const skill of await Skill.all()) {
// Skip if a command with this name already exists
if (result[skill.name]) continue
result[skill.name] = {
name: skill.name,
description: skill.description,
skill: true,
get template() {
return Skill.content(skill.name).then((content) => content ?? "")
},
hints: [],
}
}
return result
})

View File

@@ -153,4 +153,11 @@ export namespace Skill {
export async function all() {
return state().then((x) => Object.values(x))
}
export async function content(name: string) {
const info = await get(name)
if (!info) return undefined
const md = await ConfigMarkdown.parse(info.location)
return md.content
}
}

View File

@@ -2117,6 +2117,7 @@ export type Command = {
agent?: string
model?: string
mcp?: boolean
skill?: boolean
template: string
subtask?: boolean
hints: Array<string>