Skip to content

Latest commit

 

History

History
697 lines (501 loc) · 22.9 KB

File metadata and controls

697 lines (501 loc) · 22.9 KB

A friend teaches you OpenCode

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.


What OpenCode actually is

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 one
  • opencode serve — starts the server with no client at all, just an HTTP API
  • opencode 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.


The ways to use it

OpenCode has more entry points than most people realize. Here's the menu:

Terminal (TUI) — what you already know

opencode              # open in current directory
opencode ~/my-project # open in specific project
opencode -c           # continue last session
opencode -s abc123    # continue specific session

The 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.

Web interface

opencode web

This 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.

Headless server (for scripts and automation)

opencode serve --port 4096

This 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.

CLI run (one-shot prompts)

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

IDE (VS Code, Cursor, Windsurf)

Run opencode in your IDE's integrated terminal. It auto-installs the extension. Then:

  • Cmd+Esc — open/focus OpenCode in split terminal
  • Cmd+Shift+Esc — start a new session
  • Cmd+Option+K — insert file references like @File#L37-42

Attach (remote or multi-client)

# 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:4096

Both see the same sessions. Both can interact simultaneously. If you set OPENCODE_SERVER_PASSWORD, the attach command uses --password to connect.


OpenCode Go and Zen (the subscription stuff)

OpenCode can use any LLM provider. But they also offer two first-party options. Neither is required.

OpenCode Go — $10/month for open models

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.

OpenCode Zen — pay-as-you-go for premium models

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.


Making it yours

The config file

OpenCode reads config from multiple places, merging them in order of precedence:

  1. Remote config (.well-known/opencode) — organizational defaults
  2. ~/.config/opencode/opencode.json — your global settings
  3. opencode.json in the project root — project-specific overrides
  4. .opencode/ directory — agents, commands, plugins, skills, tools, themes
  5. Environment variables

Later sources override earlier ones. Project config beats global. Env vars beat everything.

Here's a sensible global config to start with:

// ~/.config/opencode/opencode.json
{
  "$schema": "https://opencode.ai/config.json",
  "model": "opencode-go/deepseek-v4-pro",
  "small_model": "opencode-go/deepseek-v4-flash",
  "autoupdate": true,
  "default_agent": "build",
  "share": "manual",
  "formatter": true,
  "lsp": true,
  "snapshot": true,
  "compaction": {
    "auto": true,
    "prune": false,
    "reserved": 10000
  },
  "permission": {
    "edit": "ask",
    "bash": {
      "*": "ask",
      "git status*": "allow",
      "git diff*": "allow",
      "npm test*": "allow",
      "rm -rf*": "deny"
    }
  }
}

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"
  }
}

A note on permissions

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.

AGENTS.md

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.

Themes

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.

Model routing

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.


Agents — the built-in ones

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:

  1. Switch to Plan (Tab) and describe what you want
  2. Iterate on the plan with feedback
  3. 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.

Creating your own agents

Sometimes the built-in agents don't fit. You can make custom ones. The easiest way:

opencode agent create

This 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

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.

Installing community plugins

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)

Writing your own

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 servers (connecting to external tools)

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.


Agent skills

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 --.


Custom commands

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

Custom tools

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

Session management (the boring useful stuff)

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 URL

Sharing 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.

Stats

opencode stats           # token usage and costs
opencode stats --days 7  # last week
opencode stats --models  # breakdown by model
opencode stats --project # current project only

Useful for the "wait, how much am I actually spending?" moment.


Other CLI commands worth knowing

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

Workflows that actually work

Research a codebase you don't know

  1. /init — let OpenCode analyze the project
  2. @explore find all files that handle authentication
  3. "@explore how does the database migration system work?"
  4. "based on what you've found, summarize the architecture"

Build a feature without chaos

  1. Switch to Plan mode (Tab)
  2. 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."
  3. Iterate on the plan: "the restore logic should check permissions first"
  4. Switch to Build mode (Tab)
  5. "Sounds good. Make the changes."
  6. /test — verify nothing broke

Code review

  1. @code-reviewer review the changes in this PR
  2. Read the feedback
  3. Switch to Build and fix what needs fixing
  4. /review — check that the fixes look right

Multi-agent parallel work

You can fire off multiple subagents at once:

  1. @explore find all API endpoints in this project
  2. @general read the documentation in /docs and summarize the authentication flow
  3. Arrow keys navigate between parent and child sessions

Quick scripting

# 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

Remote work setup

# Desktop: start web server
opencode web --port 4096 --hostname 0.0.0.0

# Laptop: attach terminal
opencode attach http://desktop-ip:4096

Both can work on the same sessions.


Things I still haven't figured out

  • 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.


Where to find more

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.