How the nvim integration works: NickvanDyke/opencode.nvim embeds the opencode TUI in a Neovim split (via snacks.nvim's terminal) and adds keymaps that pre-fill prompts with context placeholders like @cursor, @selection, @buffer, @diagnostics. Plugin spec: ~/.config/nvim/lua/plugins/opencode.lua.
Panel control
| <leader>oo | toggle the opencode panel open/closed |
| <C-\> <C-n> | when focused in the panel, exit terminal-insert mode so you can move with <C-h/j/k/l> |
| <C-l> / <C-h> | jump between code window and opencode pane (from your existing window-nav keymaps) |
Auto-reload: the plugin sets vim.o.autoread = true so files opencode edits on disk refresh in your buffer without manual :e.
Ask with context
| N <leader>oa | prompt prefilled with @cursor: — agent sees the file + cursor position |
| V <leader>oa | prompt prefilled with @selection: — agent sees only the highlighted lines |
| <leader>o+ | append @buffer — full file contents fed in |
| V <leader>oe | one-shot "Explain @selection in detail" |
Context placeholders resolve at send time. Available: @cursor, @selection, @buffer, @buffers (all open), @diagnostics, @quickfix, @cwd.
Sessions & agent control
| <leader>os | open a picker of predefined prompts (snacks picker) |
| <leader>on | start a fresh session (clears chat history) |
| <leader>oi | interrupt the agent if it's still streaming |
Scroll the conversation
| <S-C-u> | scroll messages half page up |
| <S-C-d> | scroll messages half page down |
The opencode pane is a real terminal — you can also scroll with your terminal's native scrollback (wezterm: Shift+PgUp).
Plugin spec snippet
-- ~/.config/nvim/lua/plugins/opencode.lua
return {
{
"NickvanDyke/opencode.nvim",
dependencies = { "folke/snacks.nvim" },
init = function() vim.o.autoread = true end,
config = function() vim.g.opencode_opts = {} end,
keys = { --[[ see Panel/Ask cards above ]] },
},
}
When something goes wrong
| Panel won't open | run :Lazy sync; verify opencode binary in $PATH |
| "API key invalid" | key is rejected by DeepSeek, not opencode — rotate in 1P, then load_secrets refresh, restart nvim |
| Model not found / 404 | swap model in opencode.jsonc from deepseek-v4-pro to deepseek-chat |
| File didn't reload | :set autoread — or :e to force |
About the leader: inside the opencode TUI, <leader> defaults to Ctrl+x. So <leader>n below means press Ctrl+x then n. Override in config under keybinds.leader.
INPUT typing your prompt
| Enter | submit the prompt |
| Shift+Enter / Ctrl+J | newline inside the prompt (multi-line) |
| Ctrl+C | clear the input box (does not exit) |
| Ctrl+V | paste from system clipboard |
| Ctrl+- | undo edit in the input |
| Ctrl+. | redo edit in the input |
| ↑ / ↓ | cycle through your prompt history |
SESSION control
| <leader>n | new session |
| <leader>l | list / switch sessions |
| Ctrl+R | rename the current session |
| Ctrl+D | delete the current session (also exits if no other sessions) |
| <leader>x | export the session as JSON |
| <leader>c | compact — summarise old turns to free context |
| <leader>g | show the session timeline (tree of forks) |
| Esc | interrupt the running agent |
Branching sessions: if you re-run a prompt earlier in history, opencode forks a child session. Navigate with <leader>↓ (first child), → (cycle children), ↑ (parent).
MODELS & agents
| <leader>m | pick model (filtered to your authed providers) |
| Ctrl+A | pick provider then model (longer list) |
| Ctrl+F | favourite the current model |
| <leader>a | pick an agent (build / plan / general / custom) |
| Tab | cycle to the next agent |
| <leader>t | change theme |
| Ctrl+P | command palette — every TUI action by name |
NAV messages & views
| PgUp / PgDn | page up / down through the message log |
| Ctrl+Alt+B / Ctrl+Alt+F | same, alt binding |
| <leader>y | copy the focused message |
| <leader>u | undo the last agent action (revert file edits) |
| <leader>r | redo |
| <leader>b | toggle sidebar |
| <leader>s | status view (token usage, model, agent) |
| <leader>e | open the message in $EDITOR |
EXIT
| <leader>q | quit the TUI (session is saved — resume with opencode) |
| Ctrl+C ×2 | force exit (first Ctrl+C clears input) |
| Ctrl+D | exit if input is empty |
You won't lose work. Sessions persist in ~/.local/share/opencode. opencode session list from a shell shows everything.
Config lives at ~/.config/opencode/opencode.jsonc (project-level overrides go in ./opencode.jsonc at the repo root). JSONC means JSON-with-comments. Schema is at https://opencode.ai/config.json.
Your current config
{
"$schema": "https://opencode.ai/config.json",
"model": "deepseek/deepseek-v4-pro",
"small_model": "deepseek/deepseek-chat"
}
model is the primary brain. small_model is used for cheap background work: session title generation, summarisation during compaction, search query rewriting.
Common top-level fields
model | provider/model (e.g. deepseek/deepseek-chat) |
small_model | cheap model for background tasks |
theme | TUI colour theme name |
autoshare | true/false — auto-publish sessions to web |
autoupdate | background self-update |
instructions | array of file paths to inject as system context (e.g. ["AGENTS.md", "CLAUDE.md"]) |
experimental | feature flags |
Per-agent overrides
{
"agent": {
"build": { "model": "deepseek/deepseek-v4-pro" },
"plan": { "model": "deepseek/deepseek-reasoner" }
}
}
Built-in agents: build (default, has all tools), plan (read-only, drafts plans), general (Q&A). Define custom agents under agent.<name> with their own tools + model.
Tool permissions
{
"permission": {
"edit": "ask",
"bash": "ask",
"webfetch": "allow"
}
}
allow | run without prompting |
ask | prompt every time (default for risky tools) |
deny | block entirely |
Granular patterns: "bash": { "rm *": "deny", "*": "ask" }
MCP servers
{
"mcp": {
"playwright": {
"type": "local",
"command": ["npx", "-y",
"@playwright/mcp@latest"],
"enabled": true
},
"linear": {
"type": "remote",
"url": "https://mcp.linear.app/sse"
}
}
}
opencode speaks MCP natively. Any tool you'd give Claude Code via ~/.claude/mcp.json works here too — including your Jira, Confluence, Datadog, Postgres servers.
Custom slash commands
{
"command": {
"review": {
"description": "review staged changes",
"prompt": "Review `git diff --staged` for bugs, security, and style.",
"agent": "build"
}
}
}
Trigger from the TUI input as /review. Commands can also live as files under ~/.config/opencode/commands/ — the filename becomes the command name.
Your provider is deepseek (env: $DEEPSEEK_API_KEY). opencode's catalogue exposes the four DeepSeek model IDs below. All four route to https://api.deepseek.com.
The four DeepSeek IDs
deepseek/deepseek-v4-pro | strongest general-purpose — your default. Best for coding & agentic work. |
deepseek/deepseek-v4-flash | fast & cheap — quick edits, summaries, your small_model upgrade pick |
deepseek/deepseek-chat | legacy chat model (V3-line). Always available, safe fallback if v4 errors. |
deepseek/deepseek-reasoner | extended thinking (R1-line). Slower, deeper. Pick for plans & architecture. |
If deepseek-v4-pro returns 404 against your API tier, fall back to deepseek-chat until the tier upgrades.
Picking the right one
| Refactor / multi-file edit | v4-pro |
| Quick "rename this" / explain | v4-flash |
| Plan a feature from scratch | reasoner |
| Session titles, compaction | chat (cheapest) — small_model |
Switch on the fly in the TUI with <leader>m. The new model applies from the next turn.
Where the bill comes from
DeepSeek charges per million tokens (input / output). Pricing changes — check api-docs.deepseek.com/pricing. Off-peak hours (16:30–00:30 UTC) get a steep discount on V3 chat.
| Model | Input | Output |
deepseek-chat | ~$0.27/M | ~$1.10/M |
deepseek-reasoner | ~$0.55/M | ~$2.19/M |
deepseek-v4-pro | varies | varies |
Check actual spend with opencode stats. Balance with curl -H "Authorization: Bearer $DEEPSEEK_API_KEY" https://api.deepseek.com/user/balance.
Other providers you could add
Set the relevant env var and opencode auto-detects:
$ANTHROPIC_API_KEY | Claude (Opus 4.7, Sonnet 4.6, Haiku 4.5) |
$OPENAI_API_KEY | OpenAI & o-series |
$OPENROUTER_API_KEY | OpenRouter (300+ models, one bill) |
$GROQ_API_KEY | Groq (very fast Llama / Mixtral) |
| (none) | Ollama / LM Studio / any local OpenAI-compatible server — configure under provider. |
Per-task model swap: opencode run -m anthropic/claude-sonnet-4-6 "...".
Local model recipe
{
"provider": {
"ollama": {
"npm": "@ai-sdk/openai-compatible",
"options": {
"baseURL": "http://localhost:11434/v1"
},
"models": {
"qwen2.5-coder:32b": {}
}
}
},
"model": "ollama/qwen2.5-coder:32b"
}
Useful when you need privacy or you're offline — but local models still lag DeepSeek-v4 by a wide margin on agentic tasks.
Per-agent model strategy
Best-of-both: a cheap fast model for chat, a deep reasoner for planning.
{
"model": "deepseek/deepseek-v4-pro",
"small_model": "deepseek/deepseek-v4-flash",
"agent": {
"plan": { "model": "deepseek/deepseek-reasoner" }
}
}