Skip to main content

Custom Commands

Custom commands turn frequently repeated prompts into reusable, shareable workflows. Instead of typing the same instructions every time, define a command once and invoke it with a single selection.

Defining Commands

Commands are defined in opencode.json under the command key.

{
"command": {
"test": {
"template": "Run the full test suite with coverage report and show any failures.",
"description": "Run tests with coverage",
"agent": "build",
"model": "anthropic/claude-haiku-4-5"
}
}
}

Command Fields

FieldRequiredDescription
templateYesThe prompt template sent to the LLM
descriptionNoShort description shown in the command palette
agentNoAgent to use for this command (overrides current agent)
modelNoModel to use for this command (overrides current model)

Command Templates

The template field contains the instructions sent to the LLM when the command is invoked.

Simple Template

{
"command": {
"explain": {
"template": "Explain the selected code in detail, covering what it does, how it works, and any potential issues.",
"description": "Explain code"
}
}
}

Multi-Step Template

{
"command": {
"review": {
"template": "Review the code at $ARGUMENTS for bugs, security issues, and style problems. Provide a summary of findings with severity levels.",
"description": "Review code at path"
}
}
}

Detailed Instruction Template

{
"command": {
"document": {
"template": "Add JSDoc comments to all exported functions and classes in $ARGUMENTS. Follow the project's existing JSDoc style. Include @param and @returns tags with type information.",
"description": "Add JSDoc documentation"
}
}
}

The $ARGUMENTS Variable

Use $ARGUMENTS in the template to capture user input at invocation time. The variable is replaced with whatever the user types when running the command.

{
"command": {
"refactor": {
"template": "Refactor $ARGUMENTS to improve code quality. Extract complex logic into smaller functions, add type annotations, and improve variable naming.",
"description": "Refactor code"
},
"component": {
"template": "Create a new React component named $ARGUMENTS with TypeScript support. Include proper prop types, documentation, and a basic test file.",
"description": "Create a new component"
}
}
}

When invoked, the user is prompted to provide the argument, which is substituted into the template.

Overriding Agent and Model

Commands can specify which agent and model to use, overriding the current session settings.

Agent Override

{
"command": {
"security-audit": {
"template": "Perform a security audit of the codebase. Check for OWASP Top 10 vulnerabilities, hardcoded secrets, and insecure dependencies.",
"description": "Security audit",
"agent": "security"
}
}
}

Model Override

{
"command": {
"heavy-refactor": {
"template": "Refactor the entire module at $ARGUMENTS for better architecture.",
"description": "Major refactoring",
"model": "anthropic/claude-opus-4-5"
}
}
}

Both Override

{
"command": {
"generate-docs": {
"template": "Generate comprehensive documentation for the project at $ARGUMENTS.",
"description": "Generate documentation",
"agent": "writer",
"model": "anthropic/claude-sonnet-4-5"
}
}
}

Invoking Commands

Via Command Palette

Open the command palette with Ctrl+P and type the command name or description. Select the command from the list to invoke it.

Via Slash Command

In the input bar, type / followed by the command name:

/test
/refactor src/utils.ts
/component UserProfile

Via TUI Keybinds

Commands can be bound to keyboard shortcuts in tui.json:

{
"keybinds": {
"command_test": "ctrl+alt+t",
"command_review": "ctrl+alt+r"
}
}

Keybind IDs for commands use the format command_<name> where <name> matches the command key in the config.

Markdown Command Files

Commands can also be defined as Markdown (.md) or MDX (.mdx) files in specific directories.

File Locations

  1. Project-level: .opencode/commands/ (in the project root)
  2. Global: ~/.config/opencode/commands/

Project-level files take precedence over global files.

File Format

Each file represents one command. The filename (without extension) becomes the command name.

---
description: Run tests with coverage and fail reporting
agent: build
model: anthropic/claude-haiku-4-5
---

Run the full test suite with coverage report and show any failures. Focus on:
1. Running all unit tests
2. Generating coverage report
3. Highlighting any failing tests
4. Suggesting fixes for failures

The YAML frontmatter supports the same fields as the JSON definition: description, agent, and model.

Example: Code Review Command

.opencode/commands/review.md:

---
description: Review code for bugs and style issues
agent: reviewer
---

Review the provided code for:
- Logic bugs and edge cases
- Performance issues
- Security vulnerabilities
- Style guide violations
- Missing error handling

Provide a severity rating (critical, major, minor, suggestion) for each finding.

Example: Database Migration

.opencode/commands/migrate.md:

---
description: Generate database migration
agent: builder
model: openai/gpt-5.1-codex
---

Generate a database migration for $ARGUMENTS. Include:
1. The migration SQL (both up and down)
2. A corresponding TypeScript migration file
3. Any necessary schema type updates
4. Rollback instructions

Use the existing migration patterns from the project.

Example Commands

Project Setup

{
"command": {
"setup": {
"template": "Set up the project for development. Install dependencies, create configuration files from templates, and verify the setup works.",
"description": "Initialize project setup"
}
}
}

Debug

{
"command": {
"debug": {
"template": "Debug the issue in $ARGUMENTS. Identify the root cause and suggest a fix.",
"description": "Debug an issue",
"agent": "debug",
"model": "anthropic/claude-sonnet-4-5"
}
}
}

Commit Message

{
"command": {
"commit": {
"template": "Generate a concise git commit message based on the current diff. Follow conventional commits format (type(scope): description). Consider: feat, fix, refactor, test, docs, chore.",
"description": "Generate commit message"
}
}
}

Dockerize

{
"command": {
"dockerize": {
"template": "Create a Dockerfile and docker-compose.yml for $ARGUMENTS. Use multi-stage builds, optimize layer caching, and follow security best practices.",
"description": "Dockerize a service"
}
}
}

API Client

{
"command": {
"api-client": {
"template": "Generate a type-safe API client for $ARGUMENTS. Include proper error handling, request/response types, and documentation.",
"description": "Generate API client"
}
}
}

Command Organization

For projects with many commands, group them logically:

{
"command": {
"test": { "template": "Run all tests...", "description": "Run test suite" },
"test:watch": { "template": "Run tests in watch mode...", "description": "Watch tests" },
"test:coverage": { "template": "Run tests with coverage...", "description": "Test coverage" },
"lint": { "template": "Run linter and fix issues...", "description": "Lint code" },
"lint:fix": { "template": "Auto-fix lint issues...", "description": "Auto-fix lint" },
"build": { "template": "Build the project...", "description": "Build project" },
"deploy": { "template": "Deploy to $ARGUMENTS...", "description": "Deploy to environment" }
}
}

Using colons or hyphens in command names creates logical grouping in the command palette.

Command Precedence

When commands are defined in multiple places, the resolution order is:

  1. command section in opencode.json (highest priority)
  2. .opencode/commands/ directory files
  3. ~/.config/opencode/commands/ directory files (lowest priority)

Commands with the same name in a higher-priority source override lower-priority ones.

Sharing Commands

Command definitions can be shared across your team by committing them to the project repository. Include them either in:

  • opencode.json (part of your project config)
  • .opencode/commands/ directory (part of the project's opencode configuration)

Both approaches are version-controlled and accessible to anyone who clones the repository.

Command Palette Display

The command palette (opened with Ctrl+P) shows:

  • Command name (from the key in opencode.json or filename)
  • Description (from the description field or frontmatter)
  • Agent and model overrides (if set)

Commands with descriptions are easier to discover and use. Always provide a description for non-trivial commands.

Troubleshooting

Command Not Appearing

If a command does not show up in the palette:

  1. Verify the JSON syntax in opencode.json
  2. Check that the template field is present and non-empty
  3. Ensure the config file is in a location opencode reads (project root or global config)
  4. Run opencode config show to verify the command is registered

Argument Not Substituting

If $ARGUMENTS is not being replaced:

  1. Ensure the variable is spelled exactly as $ARGUMENTS (uppercase)
  2. Check that you are providing input when the command prompts for it
  3. Verify there are no spaces or special characters breaking the substitution