I've been using OpenCode as my main coding tool for a while. A friend asked me to explain it — not the marketing version, not the "unlock your 10x potential" version. Just: what is it actually, why might you care, and what can you do with it once you get past the basics.
This is that explanation.
Most people think OpenCode is a terminal AI for writing code. It is that. But that's the wrong mental model and it'll limit how you use it.
The actual architecture is simpler and more useful: OpenCode is a headless server that can do things (read files, run commands, edit code, search the web), and you talk to it through a client. The terminal UI you see when you type opencode is just one client. There are others.
┌─────────────────────────────────────────────┐
│ OpenCode Server │
│ (reads files, runs bash, calls LLMs, etc.) │
└──────────┬──────────┬───────────┬────────────┘
│ │ │
TUI client Web client API client
(terminal) (browser) (scripts, IDE)
This matters because once you understand the server/client split, a lot of features click into place:
opencode web— starts the server and opens a browser client instead of the terminal oneopencode serve— starts the server with no client at all, just an HTTP APIopencode attach— connects a terminal client to an already-running server (maybe on another machine)opencode run "fix the auth bug"— sends a single prompt to the server, gets a result, exits
You're not locked into the terminal. You can start a server on your desktop, attach to it from your laptop. You can have the web UI open while also using the terminal. They share the same sessions and state.
OpenCode has more entry points than most people realize. Here's the menu:
opencode # open in current directory
opencode ~/my-project # open in specific project
opencode -c # continue last session
opencode -s abc123 # continue specific sessionThe Tab key switches between Build mode (can make changes) and Plan mode (can only suggest). Up/down/left/right arrows navigate between parent and child sessions. That's most of what you need for everyday use.
opencode webThis starts a local server and opens your browser. Same capabilities as the terminal, different vibe. Useful when:
- You want to scroll through long outputs without terminal limitations
- You're on a machine without a good terminal emulator
- You want to share your screen and have it look less intimidating than a TUI
You can password-protect it with OPENCODE_SERVER_PASSWORD=something opencode web. The username defaults to opencode.
The web UI also has a "See Servers" view that shows connected servers and their status. You can have multiple running.
opencode serve --port 4096This starts a server with no UI. It exposes a full REST API (OpenAPI 3.1 spec at http://localhost:4096/doc). You can talk to it with curl, a script, anything.
This is what the IDE extensions use under the hood. It's also how you'd build your own client if you wanted to.
opencode run "explain how the auth middleware works in this project"
opencode run --model opencode-go/kimi-k2.6 "add input validation to the login form"
opencode run --attach http://localhost:4096 "list all files with TODO comments"Useful for quick questions without launching the full TUI. The --attach flag is the secret weapon — it connects to an already-running server so you skip the cold start (MCP servers don't have to boot up again).
Other handy flags:
--file path/to/file.ts— attach files to the message--format json— get raw JSON output for scripting--agent plan— use a specific agent--title "auth fix"— name the session
Run opencode in your IDE's integrated terminal. It auto-installs the extension. Then:
Cmd+Esc— open/focus OpenCode in split terminalCmd+Shift+Esc— start a new sessionCmd+Option+K— insert file references like@File#L37-42
# Machine A: start server
opencode web --port 4096 --hostname 0.0.0.0
# Machine B: attach terminal client
opencode attach http://192.168.1.100:4096Both see the same sessions. Both can interact simultaneously. If you set OPENCODE_SERVER_PASSWORD, the attach command uses --password to connect.
OpenCode can use any LLM provider. But they also offer two first-party options. Neither is required.
This gets you access to a curated set of open coding models: Kimi K2.6, DeepSeek V4 Pro/Flash, Qwen3.7, MiniMax, MiMo, GLM-5.1. The models are hosted in the US, EU, and Singapore.
Usage limits are defined in dollar value, not request count:
- 5-hour limit: $12
- Weekly limit: $30
- Monthly limit: $60
What that translates to in actual requests depends on the model. DeepSeek V4 Flash is dirt cheap — you get ~31,000 requests per 5-hour window. Kimi K2.6 is pricier at ~1,150. You can track usage in the console.
If you hit the limit, you can enable a fallback to your Zen balance. Or just use the free models (DeepSeek V4 Flash Free, MiMo-V2.5 Free, Nemotron 3 Ultra Free, Big Pickle).
First month is $5, then $10. I've been using it and haven't hit the limits with normal daily work.
This gives you the big models — GPT 5.5, Claude Opus 4.8, Gemini 3.5 Flash, Grok Build, etc. You pay per token. Pricing is listed on the docs page and varies wildly (Claude Opus 4.8 is $5/M input, DeepSeek V4 Flash Free is, well, free).
Zen also supports team workspaces with admin/member roles, per-member spending limits, and the ability to disable specific models (useful if you want to forbid models that retain data).
The practical difference between Go and Zen: Go is a flat subscription for open models. Zen is pay-per-use for everything. You can use both. You can use neither and bring your own API keys.
OpenCode reads config from multiple places, merging them in order of precedence:
- Remote config (
.well-known/opencode) — organizational defaults ~/.config/opencode/opencode.json— your global settingsopencode.jsonin the project root — project-specific overrides.opencode/directory — agents, commands, plugins, skills, tools, themes- Environment variables
Later sources override earlier ones. Project config beats global. Env vars beat everything.
Here's a sensible global config to start with:
And the companion TUI config:
// ~/.config/opencode/tui.json
{
"$schema": "https://opencode.ai/tui.json",
"theme": "tokyonight",
"scroll_speed": 3,
"mouse": true,
"attention": {
"enabled": true,
"notifications": true,
"sound": true
},
"keybinds": {
"command_list": "ctrl+p",
"switch_agent": "ctrl+tab"
}
}The permission system is worth thinking about for a minute. The defaults are cautious — they'll ask before editing files or running commands. That's good. But you can get more specific:
{
"permission": {
"edit": "ask",
"bash": {
"*": "ask", // ask for everything by default
"git status*": "allow", // these are safe
"git diff*": "allow",
"npm test*": "allow",
"npm run build*": "allow",
"rm*": "deny", // never delete without asking me
"rm -rf*": "deny"
}
}
}Rules are evaluated in order — last matching rule wins. So put * first, then specific allow/deny rules after.
Run /init in any project and OpenCode analyzes it, then creates an AGENTS.md file. This is basically a README for AI — it tells OpenCode how your project is structured, what patterns you use, what to avoid. Commit it to git. It makes a difference.
Built-in themes: tokyonight, catppuccin, gruvbox, kanagawa, nord, everforest, ayu, one-dark, matrix (for the hackers).
Set with /theme in the TUI or in tui.json:
{ "theme": "catppuccin" }You can also make your own. Put a JSON file in ~/.config/opencode/themes/ with your color definitions. Your terminal needs truecolor support — check with echo $COLORTERM. If it doesn't say truecolor or 24bit, add export COLORTERM=truecolor to your shell profile.
You can set different models for different agents:
{
"model": "opencode-go/kimi-k2.6", // default for everything
"agent": {
"plan": {
"model": "opencode-go/deepseek-v4-flash" // planning is cheap
},
"build": {
"model": "opencode-go/kimi-k2.6" // building needs smarts
}
}
}I use Flash for planning and lighter work, K2.6 or DeepSeek V4 Pro for actual code generation. The cost difference adds up fast if you're not paying attention.
OpenCode comes with five agents out of the box:
| Agent | Type | What it does |
|---|---|---|
| Build | Primary | The default. Full access. Makes changes. |
| Plan | Primary | Read-only. Suggests approaches, can't edit. |
| General | Subagent | Multi-step tasks, parallel work. |
| Explore | Subagent | Fast, read-only. Searches codebases. |
| Scout | Subagent | Researches external docs and dependencies. |
Tab cycles through primary agents (Build ↔ Plan). Subagents get invoked with @agent-name — e.g., @explore find where authentication logic lives in this project.
The Plan/Build split is the workflow worth internalizing:
- Switch to Plan (Tab) and describe what you want
- Iterate on the plan with feedback
- Switch to Build (Tab) and say "sounds good, make the changes"
This alone prevents a lot of the "AI confidently does the wrong thing" problems because you catch bad approaches before code gets written.
Sometimes the built-in agents don't fit. You can make custom ones. The easiest way:
opencode agent createThis walks you through description, permissions, and model choice interactively.
Or write a markdown file in ~/.config/opencode/agents/:
---
description: Reviews code for security issues and bad patterns
mode: subagent
permission:
edit: deny
---
You are a security reviewer. When looking at code, check for:
- Input that isn't validated
- Auth that can be bypassed
- Secrets in plain sight
- Dependencies with known vulnerabilities
Be specific. Don't say "this could be more secure." Say "line 42 doesn't validate
the user ID before querying, which means any authenticated user can access any
record."Now you can invoke it: @security-reviewer check the new payment endpoint.
Common custom agents people make:
- code-reviewer — checks PRs for problems before you ship
- docs-writer — generates and maintains documentation
- security-auditor — looks for vulnerabilities
- researcher — web search and doc lookup, no code changes
Plugins are JavaScript/TypeScript modules that hook into OpenCode events. They're the most powerful customization layer — you can modify how tools work, add behavior on session events, protect sensitive files, etc.
Add them to your config:
{
"plugin": [
"opencode-wakatime", // track time in Wakatime
"opencode-notificator", // desktop notifications
"opencode-dynamic-context-pruning" // keeps context window lean
]
}The ones I actually find useful:
- opencode-dynamic-context-pruning — prunes stale tool outputs from context, saves tokens
- opencode-notificator — pings you when a long session completes or errors
- opencode-wakatime — tracks how much time you spend in AI-assisted work (if you care about that)
Plugins live in .opencode/plugins/ or ~/.config/opencode/plugins/. Here's the simplest useful one I know — protecting .env files from being read:
// .opencode/plugins/env-protection.js
export const EnvProtection = async () => {
return {
"tool.execute.before": async (input) => {
if (input.tool === "read" && input.args.filePath?.includes(".env")) {
throw new Error("Don't read .env files")
}
},
}
}Events you can hook into:
| Category | Events |
|---|---|
| File | file.edited, file.watcher.updated |
| Session | session.created, session.idle, session.compacted, session.error |
| Tool | tool.execute.before, tool.execute.after |
| Permission | permission.asked, permission.replied |
| TUI | tui.prompt.append, tui.command.execute, tui.toast.show |
| Message | message.updated, message.removed |
| LSP | lsp.client.diagnostics, lsp.updated |
The session.idle event is particularly nice — it fires when a session finishes, so you can trigger notifications, log stats, or run cleanup.
MCP (Model Context Protocol) servers add capabilities OpenCode doesn't have natively. They can search docs, query databases, scrape websites, interact with APIs — anything you can wrap in a server.
A few MCP servers actually worth enabling:
{
"mcp": {
"context7": {
"type": "remote",
"url": "https://mcp.context7.com/mcp"
},
"gh_grep": {
"type": "remote",
"url": "https://mcp.grep.app"
}
}
}- context7 — searches library/framework docs. Use in prompts: "set up a Cloudflare Worker to cache JSON responses. use context7"
- gh_grep — searches GitHub for code examples. "how do I configure custom domains in SST? use gh_grep"
The catch: MCP servers add tokens to your context window. The GitHub MCP server in particular can blow through your limits fast. Best practice is to disable them globally and enable only for agents that need them:
{
"tools": {
"context7*": false // off by default
},
"agent": {
"researcher": {
"tools": {
"context7*": true // on for the researcher
}
}
}
}You can add MCP servers interactively with opencode mcp add or manage OAuth with opencode mcp auth.
Skills are reusable instruction modules that agents load when relevant. Think of them as plug-and-play behaviors.
A skill is just a SKILL.md file in a folder:
.opencode/skills/git-release/SKILL.md
---
name: git-release
description: Create consistent releases and changelogs
---
## What I do
- Draft release notes from merged PRs
- Suggest a version bump
- Give you a ready-to-paste `gh release create` command
## When to use me
When you're preparing a tagged release. Ask if the versioning scheme isn't clear.The agent sees available skills and loads them when the task matches. You don't call them directly — the model decides when.
Skills can live in:
.opencode/skills/(project-specific)~/.config/opencode/skills/(global).claude/skills/(Claude-compatible).agents/skills/(agent-compatible)
Name rules: 1-64 characters, lowercase with single hyphens, no consecutive --.
Slash commands that trigger reusable prompts. Create them as markdown files in .opencode/commands/:
---
description: Run tests with coverage
agent: build
---
Run the full test suite with coverage. Show failures and suggest fixes.Now /test does that. You can use shell injection and arguments:
---
description: Review recent changes
agent: plan
---
Recent commits:
!`git log --oneline -10`
Review these and suggest improvements.---
description: Create a new component
---
Create a React component named $ARGUMENTS with TypeScript. Include props typing
and a basic structure.Commands I actually use:
/test— run tests and show failures/review— review recent git changes/explain @some-file.ts— walk me through a file/refactor— suggest improvements for current file
Tools are functions the LLM can call — they extend what OpenCode can do. Written in TypeScript/JavaScript in .opencode/tools/:
// .opencode/tools/database.ts
import { tool } from "@opencode-ai/plugin"
export default tool({
description: "Query the project database",
args: {
query: tool.schema.string().describe("SQL query to run"),
},
async execute(args) {
// actually query the database here
return `Ran: ${args.query}`
},
})You can write the execution logic in any language — the tool definition is TypeScript, but it can shell out to Python, Ruby, Go, whatever. Multiple tools per file get auto-namespaced (filename_toolname).
Useful custom tools people build:
- Database query tools
- API testing tools
- Log analyzers
- Document extractors
OpenCode keeps sessions that you can list, export, import, and share.
opencode session list # see all sessions
opencode session delete abc123 # delete one
opencode export # pick a session, get JSON
opencode export abc123 # export specific session
opencode export --sanitize # strip sensitive data first
opencode import session.json # from file
opencode import https://opncd.ai/s/abc123 # from share URLSharing is useful for collaboration:
/share # generates a public URL, copies to clipboard
/unshare # removes the public URL
Config options: "share": "manual" (default, you choose), "auto" (every session shared automatically), "disabled" (no sharing, ever). Project-level opencode.json with "share": "disabled" is good for work repos.
opencode stats # token usage and costs
opencode stats --days 7 # last week
opencode stats --models # breakdown by model
opencode stats --project # current project onlyUseful for the "wait, how much am I actually spending?" moment.
opencode models # list all available models
opencode models anthropic # filter by provider
opencode models --refresh # update the model cache
opencode providers # manage API keys (alias: opencode auth)
opencode providers login # add a provider
opencode providers list # show configured providers
opencode pr 42 # fetch GitHub PR #42, checkout branch, start opencode
opencode github install # set up GitHub Actions agent
opencode github run # run GitHub agent (for CI/CD)
opencode db path # where the database lives
opencode upgrade # update to latest
opencode upgrade v0.1.48 # update to specific version
opencode uninstall # remove everything
opencode uninstall --dry-run # see what would be removed first/init— let OpenCode analyze the project@explore find all files that handle authentication- "@explore how does the database migration system work?"
- "based on what you've found, summarize the architecture"
- Switch to Plan mode (Tab)
- Describe the feature: "we need to flag deleted notes in the DB instead of removing them. then show a 'recently deleted' view. users can restore or permanently delete from there."
- Iterate on the plan: "the restore logic should check permissions first"
- Switch to Build mode (Tab)
- "Sounds good. Make the changes."
/test— verify nothing broke
@code-reviewer review the changes in this PR- Read the feedback
- Switch to Build and fix what needs fixing
/review— check that the fixes look right
You can fire off multiple subagents at once:
@explore find all API endpoints in this project@general read the documentation in /docs and summarize the authentication flow- Arrow keys navigate between parent and child sessions
# One-shot question about a codebase
opencode run "what database ORM does this project use?"
# Attach to running server (no cold start)
opencode run --attach http://localhost:4096 "list files with TODO comments"
# Pipe output
opencode run --format json "explain the auth flow" > analysis.json# Desktop: start web server
opencode web --port 4096 --hostname 0.0.0.0
# Laptop: attach terminal
opencode attach http://desktop-ip:4096Both can work on the same sessions.
-
When to use web vs. TUI. The web interface is nice for scrolling through long outputs, but the terminal feels faster for everything else. I suspect the answer is "whatever doesn't annoy you" but I keep switching.
-
The right model rotation. I use K2.6 for building, Flash for planning, but I'm not sure the Flash output quality is worth the cost savings. Sometimes it hallucinates API methods that don't exist and I spend more time correcting than I saved.
-
How many custom agents is too many. I made six. I use two. The rest felt useful at the time but I never invoke them.
-
Whether context pruning actually helps. The dynamic-context-pruning plugin seems smart but I haven't measured whether it makes sessions better or just shorter.
If you figure any of these out before I do, tell me.
- Docs: opencode.ai/docs
- CLI reference: opencode.ai/docs/cli
- Discord: opencode.ai/discord
- GitHub: github.com/anomalyco/opencode
- Ecosystem page: opencode.ai/docs/ecosystem
- Community hub: opencode.cafe
The docs are good. The Discord is active. Most questions have already been asked.
If something in here is wrong or outdated, it probably is. OpenCode moves fast. Check the official docs when it matters.