feat: make skills invokable as slash commands in the TUI#11390
Conversation
There was a problem hiding this comment.
Pull request overview
This PR adds the ability to invoke skills as slash commands in the TUI, making skills more accessible through the command interface. Skills defined in SKILL.md files can now be invoked with /skillname syntax, similar to custom commands.
Changes:
- Added
Skill.content()method to extract template content from SKILL.md files - Modified
Command.list()to include skills as commands with lower priority than existing commands - Updated autocomplete UI to display skills with "(Skill)" label for clarity
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| packages/sdk/js/src/v2/gen/types.gen.ts | Added optional skill boolean property to Command type in generated SDK |
| packages/opencode/src/skill/skill.ts | Added content() method to load skill markdown content from SKILL.md files |
| packages/opencode/src/command/index.ts | Modified Command.list() to register skills as invokable commands with skill flag and template getter |
| packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx | Updated command autocomplete to display "(Skill)" label for skill-based commands |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
packages/opencode/src/skill/skill.ts
Outdated
| const md = await ConfigMarkdown.parse(info.location) | ||
| return md.content |
There was a problem hiding this comment.
The content() method lacks error handling for ConfigMarkdown.parse(), which could throw parsing errors. Unlike the addSkill() function (lines 49-57) which wraps ConfigMarkdown.parse() in a try-catch block and handles ConfigMarkdown.FrontmatterError, this method will propagate any parsing errors up to the caller. When used in Command.list() via the template getter, these errors could cause the entire command listing to fail. Consider adding error handling similar to addSkill(), or at minimum catch and log errors while returning undefined or empty string to maintain graceful degradation.
| const md = await ConfigMarkdown.parse(info.location) | |
| return md.content | |
| try { | |
| const md = await ConfigMarkdown.parse(info.location) | |
| return md.content | |
| } catch (error) { | |
| if (error instanceof ConfigMarkdown.FrontmatterError) { | |
| log.error("failed to parse skill markdown frontmatter", { | |
| path: info.location, | |
| error, | |
| }) | |
| } else { | |
| log.error("failed to load skill content", { | |
| path: info.location, | |
| error, | |
| }) | |
| } | |
| return undefined | |
| } |
| get template() { | ||
| return Skill.content(skill.name).then((content) => content ?? "") | ||
| }, | ||
| hints: [], |
There was a problem hiding this comment.
Skills are added with an empty hints array, but hints should be computed from the skill template content to support argument placeholders like $1, $2, or $ARGUMENTS. Other command types (default commands at lines 70, 79, config commands at line 93, and MCP prompts at line 119) all compute hints from their templates using the hints() function. Without proper hints, skill templates that use argument placeholders won't provide autocomplete suggestions for those arguments in the TUI. Consider computing hints after loading the skill content, or making hints a getter that computes them lazily from the template.
9059078 to
515b334
Compare
a7d015f to
2487ddd
Compare
Summary
This PR makes skills invokable as slash commands in the TUI, similar to how custom commands work.
Changes
Skill.content()method to load skill template content from SKILL.md filesCommand.list()to include skills as invokable commandsskillboolean property toCommand.InfoschemaHow it works
Users can now invoke any skill defined in SKILL.md files by typing
/skillnamein the TUI. The skill's body content (markdown after the frontmatter) is used as the command template, and arguments are substituted just like with regular commands.Skills appear in the slash command autocomplete alongside regular commands, marked with "(Skill)" label for clarity.