Skip to content

Commit 81ac41e

Browse files
authored
feat: make skills invokable as slash commands in the TUI (anomalyco#11390)
1 parent c0e71c4 commit 81ac41e

File tree

5 files changed

+25
-6
lines changed

5 files changed

+25
-6
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.source === "mcp" ? ":mcp" : serverCommand.source === "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: 18 additions & 2 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 = {
@@ -26,7 +27,7 @@ export namespace Command {
2627
description: z.string().optional(),
2728
agent: z.string().optional(),
2829
model: z.string().optional(),
29-
mcp: z.boolean().optional(),
30+
source: z.enum(["command", "mcp", "skill"]).optional(),
3031
// workaround for zod not supporting async functions natively so we use getters
3132
// https://zod.dev/v4/changelog?id=zfunction
3233
template: z.promise(z.string()).or(z.string()),
@@ -94,7 +95,7 @@ export namespace Command {
9495
for (const [name, prompt] of Object.entries(await MCP.prompts())) {
9596
result[name] = {
9697
name,
97-
mcp: true,
98+
source: "mcp",
9899
description: prompt.description,
99100
get template() {
100101
// since a getter can't be async we need to manually return a promise here
@@ -118,6 +119,21 @@ export namespace Command {
118119
}
119120
}
120121

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

packages/opencode/src/skill/skill.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export namespace Skill {
1818
name: z.string(),
1919
description: z.string(),
2020
location: z.string(),
21+
content: z.string(),
2122
})
2223
export type Info = z.infer<typeof Info>
2324

@@ -74,6 +75,7 @@ export namespace Skill {
7475
name: parsed.data.name,
7576
description: parsed.data.description,
7677
location: match,
78+
content: md.content,
7779
}
7880
}
7981

packages/opencode/src/tool/skill.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import path from "path"
22
import z from "zod"
33
import { Tool } from "./tool"
44
import { Skill } from "../skill"
5-
import { ConfigMarkdown } from "../config/markdown"
65
import { PermissionNext } from "../permission/next"
76

87
export const SkillTool = Tool.define("skill", async (ctx) => {
@@ -62,7 +61,7 @@ export const SkillTool = Tool.define("skill", async (ctx) => {
6261
always: [params.name],
6362
metadata: {},
6463
})
65-
const content = (await ConfigMarkdown.parse(skill.location)).content
64+
const content = skill.content
6665
const dir = path.dirname(skill.location)
6766

6867
// Format output similar to plugin pattern

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2116,7 +2116,7 @@ export type Command = {
21162116
description?: string
21172117
agent?: string
21182118
model?: string
2119-
mcp?: boolean
2119+
source?: "command" | "mcp" | "skill"
21202120
template: string
21212121
subtask?: boolean
21222122
hints: Array<string>
@@ -4913,6 +4913,7 @@ export type AppSkillsResponses = {
49134913
name: string
49144914
description: string
49154915
location: string
4916+
content: string
49164917
}>
49174918
}
49184919

0 commit comments

Comments
 (0)