Skip to content
This repository was archived by the owner on Feb 25, 2026. It is now read-only.

Commit 8512655

Browse files
committed
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
1 parent 252b2c4 commit 8512655

File tree

4 files changed

+27
-1
lines changed

4 files changed

+27
-1
lines changed

packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -345,8 +345,9 @@ export function Autocomplete(props: {
345345
const results: AutocompleteOption[] = [...command.slashes()]
346346

347347
for (const serverCommand of sync.data.command) {
348+
const label = serverCommand.mcp ? " (MCP)" : serverCommand.skill ? " (Skill)" : ""
348349
results.push({
349-
display: "/" + serverCommand.name + (serverCommand.mcp ? " (MCP)" : ""),
350+
display: "/" + serverCommand.name + label,
350351
description: serverCommand.description,
351352
onSelect: () => {
352353
const newText = "/" + serverCommand.name + " "

packages/opencode/src/command/index.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { Identifier } from "../id/id"
66
import PROMPT_INITIALIZE from "./template/initialize.txt"
77
import PROMPT_REVIEW from "./template/review.txt"
88
import { MCP } from "../mcp"
9+
import { Skill } from "../skill"
910

1011
export namespace Command {
1112
export const Event = {
@@ -27,6 +28,7 @@ export namespace Command {
2728
agent: z.string().optional(),
2829
model: z.string().optional(),
2930
mcp: z.boolean().optional(),
31+
skill: z.boolean().optional(),
3032
// workaround for zod not supporting async functions natively so we use getters
3133
// https://zod.dev/v4/changelog?id=zfunction
3234
template: z.promise(z.string()).or(z.string()),
@@ -118,6 +120,21 @@ export namespace Command {
118120
}
119121
}
120122

123+
// Add skills as invokable commands
124+
for (const skill of await Skill.all()) {
125+
// Skip if a command with this name already exists
126+
if (result[skill.name]) continue
127+
result[skill.name] = {
128+
name: skill.name,
129+
description: skill.description,
130+
skill: true,
131+
get template() {
132+
return Skill.content(skill.name).then((content) => content ?? "")
133+
},
134+
hints: [],
135+
}
136+
}
137+
121138
return result
122139
})
123140

packages/opencode/src/skill/skill.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,4 +153,11 @@ export namespace Skill {
153153
export async function all() {
154154
return state().then((x) => Object.values(x))
155155
}
156+
157+
export async function content(name: string) {
158+
const info = await get(name)
159+
if (!info) return undefined
160+
const md = await ConfigMarkdown.parse(info.location)
161+
return md.content
162+
}
156163
}

packages/sdk/js/src/v2/gen/types.gen.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2117,6 +2117,7 @@ export type Command = {
21172117
agent?: string
21182118
model?: string
21192119
mcp?: boolean
2120+
skill?: boolean
21202121
template: string
21212122
subtask?: boolean
21222123
hints: Array<string>

0 commit comments

Comments
 (0)