Feat: custom commands
feat: Add custom slash commands support
Summary
• Adds support for user-defined custom slash commands stored as markdown files • Commands support dynamic argument interpolation with validation • Implements automatic command discovery with scope-based priority
Overview
This PR implements a custom slash commands feature that allows users to define their own commands as markdown files in .opencode/commands/ directories. Commands are treated as prompts that can include argument placeholders which are validated and interpolated before submission.
Key Features
-
Command Discovery & Loading • Automatically discovers commands from: • Project-specific:
<project>/.opencode/commands/• User-global:~/.opencode/commands/• Supports namespaced commands via directory structure (e.g.,git/commit.md→/git:commit) • Commands are prefixed with scope (user:orproject:) to prevent conflicts • Project commands take priority over user commands with the same name • Hot-reloads commands when files change -
Argument Handling • Argument placeholders:
$ARGUMENTSor{{args}}in command content • Automatic argument count validation based on placeholder occurrences • Commands with placeholders are inserted into input field for user to add arguments • Commands without placeholders are submitted immediately • Clear error messages when wrong number of arguments provided -
Content Resolution • File references:
@{path/to/file.ts}injects file contents (relative to command file) • Arguments are interpolated individually into their respective placeholders • File content is truncated to 1MB to prevent memory issues -
Integration • Server endpoints:
/command,/command/:name,/command/resolve• TUI autocomplete support with descriptions and argument hints • Commands appear in slash command menu with proper categorization • Seamless execution as regular prompts to the AI
Implementation Details
Server-side (TypeScript):
• packages/opencode/src/command/ - Core command module
• types.ts - Type definitions and schemas
• loader.ts - Command discovery and file watching
• resolver.ts - Argument validation and content resolution
• index.ts - Public API with App.state integration
TUI-side (Go):
• internal/components/chat/editor.go - Command selection and submission logic
• Added validation to prevent submission with unresolved placeholders
• Custom commands populate input field when arguments are needed
Example Commands
Simple command without arguments:
---
description: Explain the current code
---
Please explain what this code does and how it works.
Command with arguments:
---
description: Create a function with specified parameters
argument-hint: "<name> <params> <return-type>"
---
Create a function named $ARGUMENTS that takes parameters $ARGUMENTS and returns $ARGUMENTS.
Include proper error handling and documentation.
Testing
• Unit tests for argument validation and interpolation • Tests for command loading with scope priority • Tests for file reference resolution • All resolver tests passing (8/8)
Breaking Changes
None - this is a new feature that doesn't affect existing functionality.
This PR is huge, I've been adding support for references for agents.
Does your change follow nested links?
Alos how doesn't handle relative paths?
I see workingDirectory: this.app.path.cwd, is that enough to work well in both global and local config?
Also have been wondering if agents could be used as custom commands; lots of features have overlap.
Also have been wondering if agents could be used as custom commands; lots of features have overlap.
I'd like to think custom commands should be a totally separate thing as they don't spawn a new context like an agent might. Also hoping to be able to incorporate MCP provided prompts as custom commands soon as well.
This PR is huge, I've been adding support for references for agents.
Does your change follow nested links? Alos how doesn't handle relative paths? I see
workingDirectory: this.app.path.cwd,is that enough to work well in both global and local config?
Not sure what you mean by nested links. I did update the path resolution though.
@ezynda3 yes, mcp provider support is something that I need as well.
I created a PR for prompt reference resolution for agents and modes at https://github.com/sst/opencode/pull/1317 This supports nested file references and is reusable. Please check it out.
These changes are non-trivial, though. I would wait for @thdxr's opinion regarding implementation.
hey i appreciate how much work you put into this but in the readme we try to make it clear you should chat with us before working on any large feature
this is a good feature and the design of it might even be close to perfect but we need to run stuff like this through our internal process
lot of work put into this but realistically i'll probably do it from scratch so it aligns with other things we're working on
hey i appreciate how much work you put into this but in the readme we try to make it clear you should chat with us before working on any large feature
this is a good feature and the design of it might even be close to perfect but we need to run stuff like this through our internal process
lot of work put into this but realistically i'll probably do it from scratch so it aligns with other things we're working on
Crap I should have read closer. My bad. No worries. Just had the itch and wanted to get this feature in there as it's been a while since the rewrite.
@thdxr any plans on adding commands any time soon?
would be a massive fan of this feature
TBH without this feature, I won't migrate from Claude Code. Custom commands are deeply integrated into my workflow now.
for me too this is a must have feature
you can use your claude commands in sst. if you just @./cursor/commands/your-command.md it works. it's just not discoverable with /your-command
@thdxr Hi pro, any plan for this feature?
@thdxr Hi pro, any plan for this feature?
I think they're busy planning and implementing this, here is the comment to the PR I created a few weeks ago https://github.com/sst/opencode/pull/707#issuecomment-3071380154
It's not really a small feature, so it might take them a while to get it ready on top of staying ahead of the new AI tool releases
This feature is essential to my workflow. I am eagerly awaiting it.
This is also pretty much a deal breaker for me, I've got claude code running with LiteLLM Proxy to Qwen3 Coder via openrouter and would really just like to use opencode. But commands have become core to my workflow, so when ever I try I get annoyed and bounce off back to claude.
I think proposed arguments definition is not really "user friendly":
argument-hint: "
"
Is there a reason to pass a string in place of a yaml array? I think definition like below can increase DX and be more evolutive (like define arguments types):
arguments:
name: string
params: array
return-type: string
So array type can tell opencode to ask arguments one by one for example. And typing avoid possible errors on interpolation.
Idk how all is already implemented. This is a just a suggestion on the fly 😄