Просмотр исходного кода

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
Dax Raad 2 недель назад
Родитель
Сommit
85126556b8

+ 2 - 1
packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx

@@ -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 + " "

+ 17 - 0
packages/opencode/src/command/index.ts

@@ -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
   })
 

+ 7 - 0
packages/opencode/src/skill/skill.ts

@@ -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
+  }
 }

+ 1 - 0
packages/sdk/js/src/v2/gen/types.gen.ts

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