Custom commands and keybinds in opencode
IntermediateLast updated: Oct 21, 2026
Two small surfaces with outsized payoff: custom slash commands package repeatable prompts, and keybinds remap the TUI to your hands.
Custom slash commands
Drop a markdown file in .opencode/commands/ (project) or ~/.config/opencode/commands/ (global). The filename becomes the command name:
---
description: Run tests with coverage
agent: build
model: anthropic/claude-3-5-sonnet-20241022
---
Run the full test suite with coverage report and show any failures.
Focus on the failing tests and suggest fixes.
Run it in the TUI:
/test
Frontmatter sets command options; the body is the prompt template. Custom commands sit alongside built-ins like /init, /undo, /redo, /help — and a custom command with the same name overrides the built-in.
You can also define commands in opencode.json under the command key, where template is required:
{
"command": {
"test": {
"template": "Run the full test suite with coverage report and show any failures.",
"description": "Run tests with coverage"
}
}
}
Template placeholders
$ARGUMENTS— the full argument string:/component ButtonexpandsButtoninto the template.$1,$2,$3… — positional arguments, for multi-slot templates likeCreate a file named $1 in $2 with content: $3.!`command`— run a shell command in the project root and inject its output into the prompt:
---
description: Review recent changes
---
Recent git commits:
!`git log --oneline -10`
Review these changes and suggest any improvements.
@file— pull a file’s content into the prompt automatically:Review the component in @src/components/Button.tsx.
Command options
| Key | Effect |
|---|---|
template |
The prompt sent to the LLM. Required in JSON config; the file body supplies it in Markdown. |
description |
Shown in the TUI when you pick the command. |
agent |
Which agent executes it (default: current agent). A subagent target triggers a subagent invocation. |
subtask |
true forces a subagent invocation even for mode: primary agents — keeps your main context clean. false disables it. |
model |
Per-command model override. |
Pin agent: plan on a review command for a guaranteed read-only pass over a diff.
Keybinds: tui.json
TUI keybinds live in tui.json — not opencode.json, where the legacy keybinds key is deprecated and auto-migrated (see the configuration guide). The map is action name → binding:
{
"$schema": "https://opencode.ai/tui.json",
"leader_timeout": 2000,
"keybinds": {
"leader": "ctrl+x",
"session_new": "<leader>n",
"session_compact": "<leader>c",
"agent_cycle": "tab",
"app_exit": "ctrl+c,ctrl+d,<leader>q"
}
}
- Leader key —
ctrl+xby default; press it, then the shortcut (ctrl+xthenn= new session).leader_timeoutsets the wait in ms. Use<leader>in your own binds to avoid terminal conflicts. - Value forms — one shortcut, a comma-separated list of fallbacks, an array, or an object with
key,event,preventDefault,fallthroughfor advanced cases. - Unbind — set an action to
"none"(orfalse) to disable it.
Useful actions:
| Action | Default | What it does |
|---|---|---|
agent_cycle / agent_cycle_reverse |
tab / shift+tab | Switch primary agent |
session_new, session_list, session_compact |
<leader>n, <leader>l, <leader>c |
Session management |
session_interrupt |
escape | Stop the running response |
session_child_first / session_parent |
<leader>down / up |
Enter / leave subagent sessions |
model_list, agent_list |
<leader>m, <leader>a |
Model and agent pickers |
The full action list is in the keybinds doc. One gotcha: some terminals don’t send Shift+Enter — the doc has per-terminal fixes, including Windows Terminal.
v2 differences
The v2 commands doc renames a few things: the JSON key is commands (plural), and subtask is a deprecated alias of subagent — subagent: true runs the command in a background child session. Nested directories namespace commands: .opencode/commands/team/review.md runs as /team/review. Stored templates no longer expand @file refs — attach files when invoking instead. On name collisions project commands override global ones, and command files hot-reload on save. Keybinds also move: in v2 they live in ~/.config/opencode/cli.json under keybinds — the value forms are the same (objects with key, event, preventDefault, fallthrough) but command ids switch from snake_case to dot-notation (app_exit→app.exit, input_paste→prompt.paste), and leader_timeout becomes a top-level leader.timeout sibling. See the v1↔v2 mapping.