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:

ShapeHow you talk to itWhen
TUIopencode — full-screen terminal appDefault. Day-to-day work.
Embeddednvim split via opencode.nvimYour setup. Code and agent share one screen.
Headlessopencode run "...", opencode serve, ACPScripts, CI, IDE integrations.
The mental shortcut: opencode is "Claude Code with a model switch." Same loop, same kind of tools, same per-project session model — but the brain you swap in is yours to pick.

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.

you type a prompt model deepseek-v4-pro sees system + history + tools tool call? edit / bash / read / plan / ... tool runs locally may prompt you to approve result fed back no more calls → answer streams back
Each turn loops between model → tool → result until the model stops asking for tools.

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.

Cost grows with the loop length. A "rename function" might fit in one turn; an "add a feature" can chew through many. Set a budget per task in your head and watch 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.

The boundary you control is permission, not "is there a tool." A model with the 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.

one session — switch agents with <leader>a build default · full toolset read · write · edit bash · grep · glob webfetch · todo · … deepseek-v4-pro plan read-only · drafts only read · grep · glob webfetch · todo no edit · no bash deepseek-reasoner general Q&A · no side effects read · grep · glob webfetch no write · no edit deepseek-v4-flash
Built-in agents differ in tools and model. Define your own under 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.

  1. System prompt — the agent's persona, tool docs, project rules
  2. Instruction files — anything listed in config.instructions (e.g. AGENTS.md, CLAUDE.md) gets stitched into the system prompt
  3. Session history — every prior user message, model reply, and tool result, in order
  4. Your new prompt — with @cursor / @selection / @buffer already 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.

Why compaction is necessary. History grows linearly with every turn. A long session with five 5k-token file reads is already 25k+ tokens of input on every subsequent call. Compaction trims it back down.

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 edit and bash.
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.

Self-hosted keys, self-owned consequences. With a managed product like Claude Code, your provider has guardrails. With opencode + DeepSeek, your local approval prompt is the guardrail. Worth taking seriously.

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)

DimensionClaude Codeopencode
ModelAnthropic onlyany provider (DeepSeek, OpenAI, local, …)
CostPays per token at Anthropic pricesPays per token at your provider's prices (DeepSeek ~ 1/8 of Sonnet)
SessionsPer-project, server-sidePer-project, on your disk
ToolsBuilt-in + MCPBuilt-in + MCP
Editor integrationVS Code, JetBrains, WebTUI + nvim plugin + ACP for any editor
UX polishHigherCatching 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