conceptual guide
opencode — the agent loop, in your editor
What opencode actually is
opencode is a local coding agent — same family as Claude Code, Cursor, aider — except it's open source, terminal-first, and provider-agnostic. You point it at any chat-completions model (DeepSeek, Claude, OpenAI, a local Ollama) and it gives you an agent: a thing that can read your files, edit them, run shell commands, draft plans, and chase a task across many turns without you re-pasting context.
It runs as one of three shapes:
| Shape | How you talk to it | When |
|---|---|---|
| TUI | opencode — full-screen terminal app | Default. Day-to-day work. |
| Embedded | nvim split via opencode.nvim | Your setup. Code and agent share one screen. |
| Headless | opencode run "...", opencode serve, ACP | Scripts, CI, IDE integrations. |
The agent loop
Every prompt you send kicks off the same machine. The model is allowed to request tools rather than just talking. Each tool request runs locally (reading a file, executing bash, etc.), the result is fed back to the model, and it decides what to do next. It keeps looping until it has nothing left to call — then it answers you.
The looping is the difference between an "AI chat" and an "AI agent." A chatbot stops after one reply. An agent keeps going until it actually finishes the task — which on a refactor might mean five file reads, three edits, a cargo build, a failure to parse, a re-read, and a corrective edit, all without you in the loop.
opencode stats.Sessions — history, forks, and compaction
Every conversation lives inside a session. Sessions are stored on disk in ~/.local/share/opencode (sqlite + per-session message blobs), scoped to the directory you launched opencode in. Closing the TUI does not lose work — re-open opencode in the same directory and your most recent session resumes.
Branching by rewind
If you scroll up and re-run a prompt earlier in the history, opencode forks a child session at that point instead of mutating history. The original answer is preserved on the parent branch; your new answer continues on the child. Use this when an answer went sideways and you want to try a different angle without losing the dead-end for reference. Navigate the tree with <leader>g (timeline) and the parent/child arrow keys.
Compaction
Long sessions blow past the model's context window. Press <leader>c to compact: opencode replaces the older turns with a small-model summary, freeing context for new turns. You lose verbatim recall of compacted turns but keep their conclusions. This is why the config has both a model (used for the main loop) and a small_model (used for compaction, titles, etc.) — you want the cheap one doing the summarising.
Tools — what the model can actually do
The model on its own can only emit text. Tools are the verbs you grant it: small typed functions that opencode runs on its behalf and returns results from. The built-in toolset covers everything a developer agent typically needs:
- read
- open a file (or a range of lines) and feed the contents into context
- write
- create a new file
- edit
- surgical find-and-replace on an existing file
- multiEdit
- multiple edits in one shot, atomically applied
- bash
- run a shell command (capture stdout/stderr/exit code)
- glob / grep
- find files by name or by content — cheaper than reading the whole tree
- list
- directory listing
- todoWrite
- maintain a TODO list across turns; the model uses it to track multi-step work
- webfetch
- HTTP GET against a URL (fetch docs, API examples)
- task
- spawn a sub-agent with its own context window for a focused sub-task
Tools defined by MCP servers show up alongside the built-ins. If you wire up your Jira MCP, the model gets jira_search_issues, jira_get_issue, etc., with no other config. Same with Postgres, Confluence, Datadog.
bash tool can shell-execute anything; the constraint is whether opencode asks you first.Agents — different brains for different jobs
An agent in opencode is a bundle of: a system prompt, a set of allowed tools, and a model choice. The TUI ships with a few built-ins and you can add your own under agent.<name> in config. Switch agents at any time with <leader>a; the new agent applies from the next turn onwards.
agent.<name>.The reason for multiple agents is safety meets cost. You don't want a brainstorm to accidentally edit files; you don't want every read-only question to burn the most expensive model. Different agents make the trade-off explicit.
What the model actually sees each turn
Every model call is a fresh send: opencode rebuilds a single big "messages" array and posts it. There is no hidden memory between calls. Everything the model "remembers" is in that array.
- System prompt — the agent's persona, tool docs, project rules
- Instruction files — anything listed in
config.instructions(e.g.AGENTS.md,CLAUDE.md) gets stitched into the system prompt - Session history — every prior user message, model reply, and tool result, in order
- Your new prompt — with
@cursor/@selection/@bufferalready expanded into real file content
This is why context placeholders matter: they expand on the client side. @selection in your nvim prompt becomes the actual highlighted text before the request is sent. The model sees the text, not the placeholder.
Approval model — allow / ask / deny
Tool calls are gated by your permission config. The three settings, per tool or per pattern:
- allow
- run silently. Cheap reads usually live here.
- ask
- prompt in the TUI; accept or reject each call. Default for
editandbash. - deny
- refuse and tell the model so. Use to fence off destructive shapes (
"rm *": "deny").
The default is conservative: edits and shell commands ask every time. Once you trust a workflow you can promote it to allow. For really risky shapes (force-push, drop table, prod creds) lock them to deny at the pattern level so a runaway agent can't get past you.
MCP — extending the toolset
MCP (Model Context Protocol) is the spec for plug-in tool servers. You declare an MCP server in your opencode config; opencode starts it, discovers what tools it offers, and exposes them to the model just like the built-ins. The model has no idea whether jira_get_issue is implemented in opencode core or in a Node process opencode spawned on startup — it's just a tool.
{
"mcp": {
"jira": { "type": "local", "command": ["npx", "-y", "@your/jira-mcp"] },
"postgres": { "type": "local", "command": ["pg-mcp"] },
"linear": { "type": "remote", "url": "https://mcp.linear.app/sse" }
}
}
Whatever MCP setup you already have for Claude Code is a lift-and-shift away. Same spec, different host.
Why this over Claude Code (when you keep both)
| Dimension | Claude Code | opencode |
|---|---|---|
| Model | Anthropic only | any provider (DeepSeek, OpenAI, local, …) |
| Cost | Pays per token at Anthropic prices | Pays per token at your provider's prices (DeepSeek ~ 1/8 of Sonnet) |
| Sessions | Per-project, server-side | Per-project, on your disk |
| Tools | Built-in + MCP | Built-in + MCP |
| Editor integration | VS Code, JetBrains, Web | TUI + nvim plugin + ACP for any editor |
| UX polish | Higher | Catching up fast |
The most honest framing: opencode is the agentic-coding loop, decoupled from a single vendor. You keep Claude Code for the best Claude experience, and pull opencode out when you want cheaper tokens, a local model, or just to keep your workflow vendor-portable.
Last updated: 2026-05-18 · opencode 1.15 · back to index