Skip to content

Conversation

ezynda3
Copy link
Contributor

@ezynda3 ezynda3 commented Jul 25, 2025

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

  1. 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: or project:) to prevent conflicts
    • Project commands take priority over user commands with the same name
    • Hot-reloads commands when files change

  2. Argument Handling
    • Argument placeholders: $ARGUMENTS or {{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

  3. 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

  4. 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.

@ezynda3 ezynda3 marked this pull request as draft July 25, 2025 09:10
@mpazik
Copy link

mpazik commented Jul 25, 2025

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?

@mpazik
Copy link

mpazik commented Jul 25, 2025

Also have been wondering if agents could be used as custom commands; lots of features have overlap.

ezynda3 added 11 commits July 25, 2025 15:02
- Remove allowed-tools from CommandMetadataSchema
- Delete unused parser.ts file
- Remove ParsedToolRestriction type
- Simplify implementation for this iteration
- Add app context to CommandResolver constructor
- Implement getBaseDirectory() to determine correct base path based on scope
- User commands now resolve files relative to global config directory
- Project commands resolve files relative to project root
- Fallback resolution: first try relative to command file, then base directory
- Addresses reviewer concern about path resolution for different scopes
…commands

- Update unprefixedMap to only store project commands over user commands
- Reorder getCommand lookup to check project: prefix before user: prefix
- Ensures project-specific commands override global user commands with same name
- Create self-contained test that doesn't depend on imports
- Mock file system operations to test command loading logic
- Test all three key scenarios: basic loading, namespacing, and priority
- Ensures tests can run anywhere without dependency issues
- Remove original loader.test.ts that had import issues
- Rename loader-standalone.test.ts to loader.test.ts
- Keep the self-contained test that works in CI environments
@ezynda3 ezynda3 marked this pull request as ready for review July 25, 2025 13:44
@ezynda3
Copy link
Contributor Author

ezynda3 commented Jul 25, 2025

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.

@ezynda3
Copy link
Contributor Author

ezynda3 commented Jul 25, 2025

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.

@mpazik
Copy link

mpazik commented Jul 25, 2025

@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 #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.

@thdxr
Copy link
Contributor

thdxr commented Jul 25, 2025

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

@ezynda3
Copy link
Contributor Author

ezynda3 commented Jul 25, 2025

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.

@luisrudge
Copy link

@thdxr any plans on adding commands any time soon?

@TamirShklaz
Copy link

would be a massive fan of this feature

@olafgeibig
Copy link

TBH without this feature, I won't migrate from Claude Code. Custom commands are deeply integrated into my workflow now.

@ivanthe-stack
Copy link

for me too this is a must have feature

@luisrudge
Copy link

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

@hujianxin
Copy link

@thdxr Hi pro, any plan for this feature?

@ll931217
Copy link
Contributor

ll931217 commented Aug 4, 2025

@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 #707 (comment)

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

@quantmind-br
Copy link

This feature is essential to my workflow. I am eagerly awaiting it.

@martinffx
Copy link

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.

@fmatsos
Copy link

fmatsos commented Aug 15, 2025

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 😄

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.