AI

OpenCode CLI Cheat Sheet: Commands, Sessions and Fixes (PDF)

OpenCode now ships as two different programs under the same opencode command, and most cheat sheets still document the old one. OpenCode 2 replaced the per-command server with a background service, moved export under opencode session, renamed the config keys and dropped web, attach and session sharing. The 1.x line still gets fixes through the opencode-ai npm package, so your install method decides which one you have.

Original content from computingforgeeks.com - post 167358

This OpenCode CLI cheat sheet covers OpenCode 2 first (commands, headless run, sessions, the service, keybinds, permissions, Ollama and errors) and ends with a 1.x reference. If the October Console breach brought you here, the CLI wasn’t part of it, and the breach section says which keys you should rotate anyway. We re-checked the version, help, auth and debug output on Ubuntu 26.04 against OpenCode 2.1.4 and 1.19.4 in October 2026. The run, TUI and Ollama captures come from a 16 GB M4 Mac on 2.1.1 and 1.19.3 earlier that month. By November 2026 the 2.2 line had reached 2.2.5 and added TUI features such as /goal, so check opencode --help on your build against the command map.

Check which OpenCode you have

Check the version string to see which line you are on before you copy a single command from anywhere. OpenCode 2 prints a leading v and the program name, while 1.x prints the bare number:

opencode --version

On the two builds we re-checked, the outputs looked like this, 2.x first and 1.x second:

opencode v2.1.4
1.19.4

The install method decides which one you got, because the two lines live in different packages and the 1.x install script now refuses to install a 2.x build. Use this table when a command from a tutorial fails with “Unrecognized flag” or “Unexpected positional argument”, since that almost always means the tutorial was written for the other line:

Install methodOpenCode 2OpenCode 1.x
npmnpm i -g @opencode/clinpm i -g opencode-ai
Install scriptcurl -fsSL https://opencode.ai/v2/install | bashcurl -fsSL https://opencode.ai/install | bash
Homebrewbrew install anomalyco/tap/opencode-v2 (core: brew install homebrew/core/opencode)brew install anomalyco/tap/opencode
Binary location (script)~/.opencode/bin~/.opencode/bin
Version outputopencode v2.x.y1.x.y
Docsopencode.ai/v2/docsopencode.ai/docs

Both install scripts write to the same ~/.opencode/bin directory, and the 2.x npm package also ships an opencode2 alias for the same binary. Don’t keep both lines installed through different package managers, because whichever comes first on your PATH wins, and that’s the usual reason “I upgraded but nothing changed”. Run which -a opencode to see every copy on the path. Homebrew has its own version of the trap: with the anomalyco tap installed, brew info opencode on our Mac warned that opencode shadows homebrew/core/opencode and offered the tap’s 1.x build, so name the formula in full. The tap’s opencode-v2 formula also conflicts with its 1.x opencode formula, because both install an opencode binary, so run brew uninstall opencode before you install it.

Was OpenCode hacked? The Console breach and your keys

OpenCode Console was breached, but the OpenCode CLI was not. On October 17, 2026 OpenCode said it had “a security breach last night” that hit 1.4% of Console users, all of whom had an external provider key linked, and that every one of them got an email that morning. TechCrunch reported that the attackers broke into an older system, and quoted co-founder Dax Raad’s reply: “Note this is the OpenCode Console, not OpenCode itself, those keys never come to us.” A key you gave the CLI through opencode auth login or an environment variable stays on your machine, so it was never in the store that leaked.

This is what was public as of October 26, 2026:

  • Provider keys saved in Console workspaces (OpenAI, Anthropic, Google and possibly others) may have been exposed
  • The earliest possible exposure was 00:30 UTC on October 17, per OpenCode’s email
  • The command-line tool and the open source app were not affected, per Raad via TechCrunch
  • There is no CVE, since it was a hosted-service breach, and NVD had no OpenCode entry for October when we searched

OpenCode tied the incident to a “large scale distillation attack” without saying how the two connect, and some researchers questioned that label. The bills are not in dispute. BleepingComputer collected user reports of charges from several hundred dollars up to $1,700 overnight. Per TechCrunch, Anthropic and Google blocked the keys they believed were exposed, OpenAI said it would refund API customers, and OpenCode said refunds were underway.

Rotate a key that was in Console

If you got the email, deleting the key from Console isn’t enough, because a copied key keeps working until the provider revokes it. OpenCode told affected users to revoke and replace their keys with each provider, and The Hacker News spelled out the full sequence:

  1. Revoke the key in the OpenAI, Anthropic or Google dashboard and create a new one
  2. Update every script, CI secret and tool that used the old key
  3. Check usage and billing back to October 16 for requests you didn’t make
  4. Set a spend limit or alert, and give each tool its own key

The last step is the one that pays off next time. A key shared between Console, the CLI and a CI job can’t be traced to the tool that leaked it, and revoking it breaks all three at once.

Check where the CLI gets its keys

On your own machine, opencode auth list shows every credential the service can see and where it came from. We started the 2.1.4 service with a dummy key set as ANTHROPIC_API_KEY in its environment and nothing saved:

opencode auth list

The key shows up with its source:

Anthropic  ANTHROPIC_API_KEY           environment

Keys you save with auth login are kept locally instead. The database at ~/.local/share/opencode/opencode.db, the path opencode debug paths prints as db, has a credential table, which is where saved keys appear to live. We listed the table names only and didn’t save a real login to confirm it.

After a rotation, export the new key or run opencode auth login again, then drop the stale saved account with opencode auth logout <integration>, which opens an account picker when you leave out the credential name. Restart the service too, because it keeps the environment it started with (more on that below). You should also treat two outputs as secrets, opencode auth export, whose own help says it prints stored credentials “including secrets, as JSON”, and the heap dumps covered in the service section.

Upgrade from 1.x to OpenCode 2

Recent 1.x builds no longer update themselves and never offer a 2.x build as a regular update, so the move is always explicit. Run the upgrade from the CLI, or type /update in the 1.x TUI and pick the 2.0 offer. We didn’t run this command on the lab machine, because it replaces the global install; the migration results below come from starting 2.1.1 against a copy of a 1.x home instead:

opencode upgrade --major

The 1.x help describes --major as “upgrade to OpenCode 2 and remove OpenCode 1”, so it replaces the install rather than adding a second one. We tested what carries over by copying a working 1.x home directory and starting 2.1.1 against it. Sessions, the main config and the TUI settings all came across, but they came across in different ways, so it is worth knowing what happened to each file:

  • Sessions: both 1.x sessions showed up in opencode session list on first start
  • opencode.json: read as-is and translated in memory, the file on disk is untouched
  • tui.json: rewritten into a new ~/.config/opencode/cli.json the first time the TUI opened
  • Share links: gone, since session sharing was discontinued on both lines

The cli.json it wrote from our 1.x tui.json shows the shape change, with the theme moved under name and the keybind ID renamed from session_new to the dotted session.new:

{
  "$schema": "https://opencode.ai/v2/cli.json",
  "theme": {
    "name": "tokyonight"
  },
  "keybinds": {
    "leader": "ctrl+x",
    "session.new": "<leader>n"
  }
}

Plugins are the part that won’t survive. OpenCode 2 has a new plugin API and the 2.1 release changed the plugin and SDK event contracts again, so any 1.x plugin needs a port before it loads. Popular bundles such as Oh My OpenAgent still had open “incompatible plugin API” issues against 2.x when we checked, so test a plugin-heavy setup on a spare machine before you run --major on your daily driver. The official OpenCode 2 migration guide also lists what 2.x no longer does, and two of those will surprise you: there are no LSP diagnostics, and it reads only AGENTS.md, with no CLAUDE.md fallback.

OpenCode 2 command map

Running opencode with no subcommand opens the TUI in the current directory, or in the directory you pass as the only argument. Everything else is a subcommand, and this is the full list from opencode --help on the 2.x build:

CommandWhat it does
opencode [dir]Start the TUI (flags: -c, -s ID, --prompt, --auto, --standalone, --server URL)
opencode run [msg]One-shot headless run, the CI and scripting entry point
opencode miniMinimal interactive interface with history replay
opencode session list|delete|export|importManage sessions (export and import moved here from top level)
opencode auth list|login|logout|export|import|switchCredentials and integrations; switch changes the active account
opencode modelsList every model your credentials can reach
opencode mcp list|add|auth|logoutManage MCP servers
opencode plugin list|add|check|update|removeManage plugins (a real subcommand group now)
opencode service start|stop|restart|status|get|set|unsetControl the background server every command talks to
opencode serveRun the v2 API and web server in the foreground
opencode pairPrint one-time links that connect a browser or app
opencode api <operation>Call the running server’s API by OpenAPI operation ID
opencode reloadReload configuration without restarting
opencode statsUsage, cost and tool reliability, with --json
opencode acpAgent Client Protocol server for editors
opencode debug agents|config|paths|heap-dumpResolved agents, config sources, directories, and a heap snapshot of the running service (heap-dump is new in 2.1.4)
opencode upgrade (alias update)Upgrade, with --method curl|npm|pnpm|bun|yarn|vp|brew|winget
opencode uninstallRemove the install but keep sessions, config and state

The global flags apply to every subcommand. --wizard walks you through a command’s options interactively, --completions zsh (or bash, fish, sh) prints a completion script, and --log-level plus --print-logs send logs to stderr. Server logs only print with --standalone, because without it the server is the background service and logs to its own file.

Run OpenCode headless with opencode run

opencode run sends one message, lets the agent work through its tools and exits, which makes it the piece you wire into scripts, cron jobs and CI. We planted a bug in a two-line calc.py (an add function that subtracts) and asked for a review:

opencode run -m anthropic/claude-haiku-5-5 "What does calc.py do? Is there a bug? Answer in two sentences, do not edit files."

In about four seconds of wall time the agent globbed for the file, read it and answered. Each line starting with > names the agent and model for that step, so you can tell when a subagent or a model switch took over:

> build · claude-haiku-5-5
✱ Glob "**/calc.py" 1 match
> build · claude-haiku-5-5
→ Read calc.py
> build · claude-haiku-5-5
`calc.py` defines a single function, `add(a, b)`, but it returns `a - b`, so it actually subtracts. Yes, there is a bug: the return statement should be `return a + b`.

To keep going in the same conversation, add -c to continue the last session. The follow-up below edited the file without asking, because the default build agent in OpenCode 2 allows every tool except reading .env files (more on that under permissions):

opencode run -c -m anthropic/claude-haiku-5-5 "Fix the bug in calc.py."

The edit prints as a unified diff. That’s also what you’ll see in CI logs, so you can review the change without opening the repo:

> build · claude-haiku-5-5
← Edit calc.py
Index: calc.py
===================================================================
--- calc.py
+++ calc.py
@@ -1,2 +1,2 @@
 def add(a, b):
-    return a - b
+    return a + b

> build · claude-haiku-5-5
I fixed `calc.py` by changing `return a - b` to `return a + b` in `add`, so the function now adds its arguments.

These are the run flags you will reach for most. The reasoning effort no longer has its own flag in OpenCode 2, because it now rides on the model string after a #, and -m anthropic/claude-haiku-5-5#high worked in our test where 1.x needed --variant high.

FlagPurpose
-m provider/model#variantModel, with an optional reasoning variant after #
-c / -s IDContinue the last session, or a session by ID (created if it doesn’t exist)
--forkCopy the session before continuing, so the original stays untouched
-f FILEAttach a file to the message
--agent NAMERun as build, plan or a custom agent
--format jsonEmit newline-delimited JSON events
--title / --thinkingSet the session title, show thinking blocks
--autoApprove every permission that isn’t explicitly denied
--standalone / --server URLUse a private server, or a specific one, instead of the background service

Attaching a file is the quickest way to hand a log to the agent without a copy-paste step, and it is what the “opencode include file” searches are after:

opencode run -m anthropic/claude-haiku-5-5 -f app.log "Summarise this log in one sentence."

Our two-line log came back as a one-sentence summary. You’ll get the same from a failed CI job’s log, which saves you pasting it into the TUI:

> build · claude-haiku-5-5
The log shows a database timeout error after 30 seconds on October 20, 2026, followed by a successful retry.

The file content goes into the session like any other message, so a -c follow-up can ask about the same log without attaching it again.

Parse the JSON event stream

With --format json every event is one JSON object per line carrying type, timestamp, sessionID and part. A run that read one file and then answered emitted five events in this order:

  1. step_start
  2. tool_use (the read call)
  3. step_finish with reason tool-calls, tokens and cost
  4. step_start
  5. text with the answer

There was no closing step_finish after the final text in 2.1.1, so a script that sums cost from step_finish events will undercount. Pull just the answer with jq:

opencode run --format json "Read calc.py and say in 5 words what it does." | jq -r 'select(.type=="text") | .part.text'

For cost, read the session afterwards with opencode stats --json or opencode session export <ID>, both of which carry the full token and cost totals. Cost and tokens sit inside part, so jq -c 'select(.type=="step_finish") | .part | {reason, cost, tokens}' returned this for the one step_finish we got:

{"reason":"tool-calls","cost":0.00015025,"tokens":{"input":4,"output":120,"reasoning":0,"cache":{"read":7860,"write":90}}}

In CI, add < /dev/null to the run line. One of our scripted runs with an inherited stdin produced no output at all, and the same command with stdin closed worked every time.

Manage sessions: fork, delete, export

Sessions are scoped to the project you run them in, so opencode session list shows only the top-level sessions for the current directory, newest first. The output is tab-separated (ID, title, last update), which makes it easy to script:

opencode session list

Our project had one session at this point, titled automatically from the first prompt. The outputs in this section come from separate test runs, so the session IDs differ between examples:

ses_ea4d19b42ffebmOtnf8jFLoK01	Database timeout error log review	10/21/2026, 12:18:50 AM

Forking is how you try a different direction without losing the original thread. Pass the session ID with --fork and the new message goes into a copy:

opencode run -s ses_ea4d19b42ffebmOtnf8jFLoK01 --fork "Now suggest one config change, one sentence."

Now the list shows the copy with a (fork #1) suffix and the original unchanged underneath it:

ses_ea4d1921bffe41ImCuhocOCT6t	Database timeout error log review (fork #1)	10/21/2026, 12:18:53 AM
ses_ea4d19b42ffebmOtnf8jFLoK01	Database timeout error log review	10/21/2026, 12:18:50 AM

Inside the TUI, /fork does the same for the open session, and the mini help lists -c --fork to fork the last one in the minimal interface. There is no “delete all” subcommand, but because the list is tab-separated, one pipeline clears a project. Deleting a session also deletes its child sessions, so check the list first:

opencode session list | cut -f1 | xargs -n1 opencode session delete

Each deletion prints a confirmation, and a follow-up session list returned nothing:

Session ses_ea4f0d9c7ffe5r0psKiAbSCMx3 deleted
Session ses_ea4f118acffexZOOTrtqoJn36V deleted

Export writes the whole session as JSON to stdout, including metadata, cost, tokens and every message with its tool calls. According to its help, import accepts a file or a URL, which is how you move a session to another machine now that share links are gone:

opencode session export ses_ea4eace0bffebWnVM9g2sCr1Xf > session.json
opencode session import session.json

Read the export before you send it to anyone. Ours carried absolute file paths, the shell commands the agent ran with their output, and the provider’s opaque reasoning signatures, so treat it like a log file, not a summary.

Control the background service

OpenCode 2 runs one background server per user, and the TUI, run, models and most other commands connect to it instead of starting their own. It starts on demand and keeps running after the command exits. On our Mac it listened on 127.0.0.1:49374, and since 2.1.3 the changelog puts the Windows service on port 28894. That’s why the second command is faster than the first, and also why it shows up in lsof long after you closed the TUI.

CommandUse it to
opencode service statusPrint the URL if it’s running, stopped if not
opencode service start|stop|restartControl it by hand
opencode service getShow its saved settings as JSON
opencode service set port 49375Move it to another port
opencode service set hostname 0.0.0.0Accept LAN connections (password-protected, firewall it)
opencode reloadRe-read config without a restart
--standalone on any commandSkip the service and use a private, short-lived server

The service keeps the environment it started with, which bites you if you set a variable on a single command. We started the service, then ran OPENCODE_DISABLE_PROJECT_CONFIG=1 opencode debug config, and the project config still loaded. After OPENCODE_DISABLE_PROJECT_CONFIG=1 opencode service restart it was gone, and it stayed gone for later commands that did not set the variable, until a plain opencode service restart brought it back. So export the variable and restart the service, or add --standalone for a one-off. To make a variable survive later restarts, the v2 network docs use opencode service set env NAME value and then opencode service start, with service unset env NAME to remove it.

opencode pair replaces the old web and attach pair. It prints a one-time link for a browser or the desktop app, plus the SSH tunnel to use on a remote box:

opencode pair

Each link works once and expires after five minutes (the token is replaced here):

  Open a link to connect. Links work once and expire in 5 minutes.

  http://127.0.0.1:49375/auth/connect/<one-time-token>

  Over SSH? Forward the port, then open the link on your machine:
  ssh -L 49375:127.0.0.1:49375 <host>
  If port 49375 is busy locally, forward another port and use it in the link.

  To connect from other devices, run `opencode service set hostname 0.0.0.0`.

The SSH forward is the safe way to reach a server’s OpenCode from your laptop. Per its help text, opencode pair --remote goes further and exposes the server to the internet through OpenTunnel. Without an interactive terminal it also needs --yes, so keep it for machines where that is acceptable. For scripts, opencode api calls the running server by OpenAPI operation ID, for example opencode api session.list --param limit=2, and returns raw JSON.

Capture a heap dump of the service

When the service misbehaves, with memory creeping up or a hang you can’t explain, opencode debug heap-dump snapshots the running server’s heap without restarting it and prints the file path. Add --directory to write it somewhere other than the log directory, or --server URL to point it at another server:

opencode debug heap-dump

On our idle 2.1.4 service it took about three and a half seconds and wrote a 47 MB snapshot into the log directory, with mode 0600 so only your user can read it:

/home/josphat/.local/share/opencode/log/heap-1962-2026-10-26T075000215Z.heapsnapshot

That permission matters more than it looks. The service from the auth test above was still holding the dummy key in its environment, so we changed to the log directory, where that dump was the only snapshot, and searched it for the key:

cd ~/.local/share/opencode/log
grep -c canary-key-4003-not-real heap-*.heapsnapshot

grep found one match, so the key sits in the snapshot in plain text:

1

Never attach a heap dump to a GitHub issue or a support ticket as it is. Delete it when you are done, and if one has already left your machine, rotate every key the service had loaded. The same restart test showed the environment rule again: after a plain opencode service stop and start without the variable, opencode auth list went back to “No authenticated integrations”.

Learn the OpenCode 2 TUI keybinds

The leader key is still ctrl+x, so ctrl+x m means press ctrl+x, release, then m. We pulled this table from the command palette (ctrl+p) and status bar of a running 2.x TUI with an open session, so every binding is the shipped default:

ActionKeys
Command palettectrl+p
Cycle agents (build, plan)shift+tab
Switch model / agentctrl+x m / ctrl+x a
Cycle model variant (reasoning effort)ctrl+t
New session / switch sessionctrl+x n / ctrl+x l
Open a session or projectctrl+o
Rename sessionctrl+r
Close tab / reopen closed tabctrl+x w / ctrl+shift+t
Jump to a message (timeline)ctrl+x g
Undo previous messagectrl+x u
Compact sessionctrl+x c
Copy last reply / export transcriptctrl+x y / ctrl+x x
Open external editorctrl+x e
Terminal pane / select terminalctrl+x t / ctrl+x ↓
Queue a prompt behind the running onectrl+x enter
Hide sidebar / view statusctrl+x b / ctrl+x s
Quitctrl+c, ctrl+d or ctrl+x q

Two habits from 1.x break here. Tab no longer switches agents (it’s shift+tab), and per the v2 docs Enter while the agent is working now steers the active run instead of waiting, which is why ctrl+x enter exists for prompts you want queued. The v2 docs also list ctrl+b to send a blocking tool to the background and ctrl+alt+k for a which-key overlay. Rebind any of these in ~/.config/opencode/cli.json using the dotted IDs (session.new, model.list), because the 1.x underscore names are dropped on migration.

Use the slash commands

Type / in the prompt to get the list. Several commands only appear once a session is open (/compact, /undo, /fork, /timeline, /copy, /export, /rename, /terminal), so an empty project shows fewer. This is the full set from 2.1.1, with related commands grouped in one row:

CommandWhat it does
/new, /clearStart a new session, or clear the current one
/sessions, /resume, /continueSwitch session
/open, /project, /projectsOpen a session or project
/fork / /renameFork or rename the open session
/timeline / /undoJump to a message, undo the previous one
/compactSummarise history to free context
/copy / /exportCopy the last reply, export the transcript
/diffOpen the diff viewer
/reviewReview changes: commit, branch or pr, uncommitted by default
/btwOne-shot side question that isn’t added to the conversation
/models / /agentsSwitch model or agent
/effort, /variants, /thinkingSwitch the model variant
/connectConnect a provider or integration
/mcps, /skills, /pluginsManage MCP servers, skills, plugins
/initGuided AGENTS.md setup for the repo
/cdChange working directory
/terminal / /editorNew terminal pane, open the external editor
/worktreesManage workspaces
/settings, /themes, /status, /stats, /debugSettings, theme, status, usage, debug info
/pair, /webPair a browser or device
/reload / /restartReload config, restart the service
/updateUpdate OpenCode
/help, /exit, /quit, /qHelp and quit

/share is gone from both lines, and /btw is the one new command worth building a habit around. Ask “what does this flag do?” mid-task and the answer does not pollute the context the agent is working from. Inside the prompt, @ still opens the fuzzy file picker, so @src/auth.ts attaches a file the way -f does on the command line.

Translate a 1.x config to OpenCode 2

OpenCode 2 still reads opencode.json from ~/.config/opencode/ and from the project root, and it still accepts the 1.x shapes. You don’t have to rewrite a working file on day one. opencode debug config prints every config source together with the version 2 form it was translated into. That makes it the fastest way to learn the new keys from your own file:

opencode debug config

Our 1.x file had a permission object, a custom agent and a disabled MCP server. This is what 2.1.1 turned it into, with bash renamed to shell and enabled: false flipped to disabled: true. Note that the translation keeps the original order, so the git push* deny sits before the catch-all allow and never wins. Don’t copy it as a working rule set:

"permissions": [
  { "action": "shell", "resource": "git push*", "effect": "deny" },
  { "action": "shell", "resource": "*", "effect": "allow" },
  { "action": "edit", "resource": "*", "effect": "allow" }
],
"agents": {
  "reviewer": {
    "description": "Reviews diffs, never edits",
    "mode": "subagent",
    "permissions": [{ "action": "edit", "resource": "*", "effect": "deny" }]
  }
},
"mcp": {
  "servers": {
    "fs": {
      "type": "local",
      "command": ["npx", "-y", "@modelcontextprotocol/server-filesystem", "."],
      "disabled": true
    }
  }
}

The rows marked “tested” came out of debug config on our own files, including a separate 1.x Ollama provider block and the cli.json the TUI wrote. The rest come from the migration guide, so confirm them against your file the same way:

1.xOpenCode 2Source
permission (object per tool)permissions (ordered array of action, resource, effect)tested
permission bashaction shelltested
agentagentstested
mcp.<name>, enabled: falsemcp.servers.<name>, disabled: truetested
provider, npm, options.baseURLproviders, package: "aisdk:...", settings.baseURLtested
tui.json, session_newcli.json, session.newtested
permission task, write/patchsubagent, editdocs
agent prompt, maxSteps, disablesystem, steps, disableddocs
command, plugincommands, pluginsdocs
small_modelagents.title.modeldocs
autoupdateupdate (disable, notify, auto)docs

Keep "$schema": "https://opencode.ai/config.json" at the top of opencode.json, because the obvious guess, https://opencode.ai/v2/config.json, returned a 404 when we tried it. The new CLI file is the only one with a v2 schema URL (https://opencode.ai/v2/cli.json). It is global only, with no project-level copy, and OPENCODE_CLI_CONFIG_CONTENT overrides it inline.

The 2.1 releases also added three experimental switches, all under experimental in opencode.json. The changelog is the source for what each one does. We only checked that OpenCode accepts and lists the first two, not that they change a long session:

KeyEffectSince
background_compaction: truePrepares the compaction summary in the background as a session nears its threshold; summaries that go unused still cost tokens2.1.4
todo: trueGives models the todowrite tool and shows their task list in the session and sidebar2.1.0
location_idle_timeout: "4 hours"When inactive location services unload, or false to keep them loaded2.1.2

With the first two set in a project file, opencode debug config listed it as a document source with both keys and no error:

  {
    "type": "document",
    "path": "/home/josphat/proj/opencode.json",
    "info": {
      "$schema": "https://opencode.ai/config.json",
      "experimental": {
        "todo": true,
        "background_compaction": true
      }
    }
  }

Leave background_compaction off on pay-per-token providers until you have watched what it costs on your own sessions, because it spends tokens on summaries before it knows it needs them.

Set permissions for agents

Permissions in OpenCode 2 are an ordered list where the last matching rule wins, so put the broad rule first and your exceptions after it. The defaults are permissive. On a fresh install, opencode debug agents showed the build agent allowing everything (* on *) except an ask on *.env and *.env.* reads and a deny on the browser tool. The plan agent denies edit everywhere except its own plan directory. The migration guide also notes that 2.x allows external_directory access outside the project by default where 1.x asked, so add { "action": "external_directory", "resource": "*", "effect": "ask" } if you want that prompt back. If you run OpenCode on anything that matters, write your own rules:

vim opencode.json

We tested this exact file. It allows shell commands, asks before a push, blocks rm -rf and sudo, protects env files from edits and adds a read-only reviewer subagent:

{
  "$schema": "https://opencode.ai/config.json",
  "permissions": [
    { "action": "shell", "resource": "*", "effect": "allow" },
    { "action": "shell", "resource": "git push*", "effect": "ask" },
    { "action": "shell", "resource": "rm -rf*", "effect": "deny" },
    { "action": "shell", "resource": "sudo *", "effect": "deny" },
    { "action": "edit", "resource": "*.env*", "effect": "deny" }
  ],
  "agents": {
    "reviewer": {
      "description": "Reviews diffs and never edits",
      "mode": "subagent",
      "permissions": [{ "action": "edit", "resource": "*", "effect": "deny" }]
    }
  }
}

We asked the agent to run sudo -n true with this file in place. The tool call failed with Error: Permission denied: shell, and the model reported the rejection instead of looking for a way around it. opencode run --agent reviewer started the custom agent from the project file. opencode debug agents only listed the built-in ones in our test, though, so start a project agent with --agent to confirm it loads. Remember that --auto approves every ask but still honours every deny, so deny rules are the ones that protect you in unattended runs.

Rules stop the agent doing damage, but they don’t make its output any better. That part is review discipline, and Beyond Vibe Coding (paid link) by Addy Osmani (O’Reilly, 252 pages) is the most tool-agnostic book on it. It covers setting constraints for the model and reading its diffs critically, and it is one of our best AI coding books picks. It works a level above flags, so it suits a multi-provider tool like OpenCode better than a single-vendor guide.

Connect a local Ollama model

Ollama works as an OpenAI-compatible provider. In OpenCode 2 the block lives under providers, and the SDK package name takes an aisdk: prefix. Add it to the project’s opencode.json (we ran Ollama on port 11435 to keep it isolated, the default is 11434):

vim opencode.json

Match the model key to the Ollama tag exactly, colon included:

{
  "$schema": "https://opencode.ai/config.json",
  "providers": {
    "ollama": {
      "name": "Ollama (local)",
      "package": "aisdk:@ai-sdk/openai-compatible",
      "settings": { "baseURL": "http://127.0.0.1:11435/v1" },
      "models": { "qwen3.5:4b": { "name": "Qwen 3.5 4B" } }
    }
  }
}

Reload the config, check that the model is listed, then point a run at it with the ollama/ prefix:

opencode reload
opencode models | grep ollama
opencode run -m ollama/qwen3.5:4b "Fix the bug in calc.py. mul must multiply."

The 4B model found the bug and edited the file, the same result the hosted model gave (output trimmed to the edit):

> build · qwen3.5:4b
✱ Glob "**/calc.py" 1 match
> build · qwen3.5:4b
→ Read calc.py
> build · qwen3.5:4b
Found the bug! The `mul` function should be multiplying with `*`, not adding with `+`.
← Edit calc.py
@@ -1,2 +1,2 @@
 def mul(a, b):
-    return a + b
+    return a * b

It took 4 minutes 24 seconds on the 16 GB M4, and almost all of that was the first request. OpenCode sent two requests together at the start, and the Ollama log shows them taking 3 minutes 41 seconds and 4 minutes 3 seconds to get through the agent’s system prompt and tool definitions, about 5,400 tokens. The three calls after that took 4 to 9 seconds each. The first attempt never answered, because Ollama sized the context at 4,096 tokens for this machine’s 11.8 GiB of usable GPU memory, smaller than the prompt itself, and our 120-second timeout killed it. Start Ollama with OLLAMA_CONTEXT_LENGTH=16384 or higher for agent work, and pick models from the Ollama models cheat sheet by the memory you actually have. The Ollama commands cheat sheet covers the server side. Hermes Agent takes the same custom-endpoint route to Ollama; the Hermes Agent cheat sheet has the tested config keys and the OOM fix for a 16 GB box.

If you would rather not run models locally, opencode models also listed free hosted models under the opencode/ prefix. These three were in the list on our install:

  • opencode/big-pickle
  • opencode/glm-5.3-flash-free
  • opencode/deepseek-v4.1-flash-free

Free models come and go, so check opencode models before you script against one. For paid models, the OpenAI-compatible API guide shows how to route OpenCode through a single key. To pool free-tier keys from OpenRouter, Gemini and Groq behind one provider entry instead, self-host FreeLLMAPI and point an OpenAI-compatible provider entry at its /v1 endpoint, using the 2.x providers form of the Ollama block above.

Set environment variables

The OpenCode 2 docs have no environment variable table, so we pulled the names compiled into each binary. The current 2.x binary references 48 OPENCODE_* names against 78 in the current 1.x build, and 49 of the 1.x names aren’t referenced by the 2.x binary at all, 21 of them experimental flags. The 2.x count hasn’t moved since 2.1.1 because one name came in as another went out: OPENCODE_DISABLE_FREE_MODELS was added and OPENCODE_RELEASE_HIGHLIGHTS dropped. These are the ones you’ll use. Rows marked tested were run on 2.1.1, and the unmarked ones are names compiled into the binary with the meaning the 1.x docs give them:

VariableEffect
OPENCODE_CONFIG_CONTENTInline JSON config, added as an extra source (tested)
OPENCODE_DISABLE_PROJECT_CONFIG=1Ignore the project’s opencode.json; OPENCODE_CONFIG_PROJECT_DISABLE=1 works too (tested, needs a service restart)
OPENCODE_CLI_CONFIG_CONTENTInline cli.json (theme, keybinds), per the docs
OPENCODE_DBMove the SQLite database (tested with debug paths db)
ANTHROPIC_API_KEY, XAI_API_KEY, HF_TOKENProvider keys, shown as environment in auth list (tested)
OPENCODE_CONFIG, OPENCODE_CONFIG_DIRPoint at another config file or directory
OPENCODE_LOG_LEVELLog level, replaces the old logLevel key
OPENCODE_SERVER_PASSWORDBasic auth for the server
OPENCODE_DISABLE_AUTOUPDATESkip update checks
OPENCODE_DISABLE_FREE_MODELS=1Remove zero-cost models from the catalogue, defaults and fallbacks (2.1.2 changelog; name present in the binary)
OPENCODE_DISABLE_CLAUDE_CODEIgnore .claude directories and skills
HTTPS_PROXY, NO_PROXY, NODE_EXTRA_CA_CERTSCorporate proxy and private CA

These 1.x variables aren’t referenced by the 2.x binary, so a 1.x CI job that sets them silently does nothing after the upgrade:

  • OPENCODE_PERMISSION
  • OPENCODE_AUTO_SHARE
  • OPENCODE_TUI_CONFIG
  • OPENCODE_PURE
  • OPENCODE_SERVER_USERNAME
  • every OPENCODE_EXPERIMENTAL_* flag

Move permissions into the permissions array and the TUI settings into cli.json. Variables that change the server’s behaviour follow the service rule from earlier, so set them with opencode service set env or export them and restart the service. Behind a proxy, the v2 network docs say NO_PROXY must include localhost,127.0.0.1,::1, because the CLI reaches the service over loopback.

Directories haven’t moved. opencode debug paths prints them, and the ones you will touch are ~/.config/opencode (config, cli.json, service.json), ~/.local/share/opencode/opencode.db (sessions) and ~/.local/share/opencode/log. Clearing the cache means deleting ~/.cache/opencode. On our installs it held only models.json (the model catalogue) and a bin directory, and OpenCode rebuilds both on the next start, so it’s safe to remove while the service is stopped.

Fix common OpenCode errors

Every error below was reproduced in our lab, except the free-tier one, which comes from the project’s issue tracker and is marked as such.

Error: “Managed service port 49374 on 127.0.0.1 is already in use by another process”

Something else holds the service port, usually a second OpenCode install, another user’s service, or one left behind by a sudo opencode run. Find the owner first:

lsof -nP -iTCP:49374 -sTCP:LISTEN

If it’s a stale OpenCode, stop it with opencode service stop from the account that started it. If both need to run, move this one to another port and start it again, which is what fixed it for us:

opencode service set port 49375
opencode service start

The service came up on the new port and every later command used it without extra flags. The port is saved in ~/.config/opencode/service.json, so you only set it once per user.

Error: “Unrecognized flag: –dir in command opencode run”

You are running a 1.x command on OpenCode 2. --dir and --pure were removed from run, so cd into the project first, and debug plugins with opencode plugin list and remove instead of --pure. Tools that wrap OpenCode and still pass these flags fail the same way, which is the “could not start opencode: exit 1” message some editor integrations show.

Error: “ENOENT: no such file or directory, chdir … -> ‘web'”

OpenCode 2 has no web or top-level export command, so it treats the word as a directory to open and fails to chdir into it. opencode attach http://... fails similarly with “Unexpected positional argument”. Use opencode pair for the browser, opencode --server URL to attach to a server, and opencode session export for exports.

Error: “Permission denied: shell”

A deny rule matched the command. That’s correct behaviour if you wrote the rule, so read opencode debug config to find which one matched. Rules are evaluated in order with the last match winning, so a broad allow placed after a specific deny silently cancels the deny.

Ollama model never answers

No error appears in OpenCode. The request sits until your timeout, and the Ollama log shows POST "/v1/chat/completions" ending in a 500 when the client gives up. The cause is a context window smaller than the agent’s prompt plus a slow cold start, and the fix is the larger OLLAMA_CONTEXT_LENGTH from the Ollama section with a timeout of several minutes for the first call.

Uninstall leaves the opencode command behind

opencode uninstall stops the service and removes the cache, but it keeps your sessions, config and state on purpose. It also can’t always tell how the binary was installed. On our npm install it printed “Could not detect the installation method. Remove the installation manually after cleanup.” Run which -a opencode to find every copy, then remove each one with the tool that installed it:

  • npm: npm rm -g @opencode/cli or npm rm -g opencode-ai
  • Homebrew: brew uninstall opencode or brew uninstall opencode-v2
  • install script: delete ~/.opencode/bin

Run opencode uninstall --dry-run first if you want to see what it will touch.

Error: “OpenCode’s free tier can only be used from within OpenCode”

This one comes from GitHub issue #56324, one of the most-discussed 2.x issues, not from our lab. Maintainers suspected client causes such as a custom agent that denies shell or read, then concluded it was most likely network-dependent, and the issue was closed as not planned. Test on another network without a VPN before you change config. If you never meant to use a free model in the first place, set OPENCODE_DISABLE_FREE_MODELS=1 (added in 2.1.2) and restart the service, so no default or fallback can land on one.

Keep a 1.x quick reference

You’ll probably keep some machines on 1.x for a while, especially anywhere your plugins hold up the upgrade. These are the commands and keys that differ, checked against the 1.x help output and docs:

TaskOpenCode 1.xOpenCode 2
Export / import a sessionopencode export ID / opencode import fileopencode session export ID / session import
Browser UIopencode webopencode pair
Attach to a serveropencode attach URLopencode --server URL
Run in another directoryopencode run --dir PATHcd PATH first
Reasoning effort--variant high-m provider/model#high
Pluginsopencode plugin NAME, --pureopencode plugin add|list|remove
Session listTakes -n and --format jsonCurrent project, tab-separated
Auth commandopencode providers (alias auth)opencode auth
GitHub agent, PR checkoutopencode github, opencode pr NNot in the CLI
Database shellopencode dbopencode debug paths db for the path
Switch agent in the TUITabshift+tab
TUI settings filetui.json (global or project)cli.json (global only)
Upgrade within the lineopencode upgrade (stays on 1.x)opencode upgrade

The 1.x keybinds that carry over unchanged include the leader ctrl+x followed by n, l, m, a, c, u, e, x, y, b, s and q, plus ctrl+p for the palette. On 1.x, run also takes --auto and -i (split-footer interactive mode), and the base command takes --mini. The 1.x line still gets security updates, and 1.19.4 only moved the bundled Bun runtime to 1.4.4 for its TLS, HTTP and WebSocket fixes, the same bump 2.1.3 shipped, so update a 1.x machine even if 2 has to wait. When you are ready to move, opencode upgrade --major is the way across. For a broader comparison with the other agents, the Claude Code cheat sheet, Codex CLI cheat sheet and OpenCode vs Claude Code vs Cursor cover the same ground for each tool, and the OpenCode setup guide walks through a first install on Linux.

Keep reading

Ollama Models Cheat Sheet: Best Models by VRAM, Tested (PDF) AI Ollama Models Cheat Sheet: Best Models by VRAM, Tested (PDF) Open Source LLM Comparison Table (2026) AI Open Source LLM Comparison Table (2026) Claude Code Cheat Sheet: Commands, Shortcuts and Flags (PDF) AI Claude Code Cheat Sheet: Commands, Shortcuts and Flags (PDF) Best Weather Stations for Home Assistant: Ecowitt, Tempest, Davis Automation Best Weather Stations for Home Assistant: Ecowitt, Tempest, Davis Best Water Leak Sensors and Auto Shutoff Valves for Home Assistant Automation Best Water Leak Sensors and Auto Shutoff Valves for Home Assistant Best Ansible Books to Master Automation in 2026 Ansible Best Ansible Books to Master Automation in 2026

Leave a Comment

Press ESC to close