OpenCode 怎么用
一个跑在终端里的开源 AI 编程代理:一条命令装上、一次 /connect 接上模型,然后让它替你读代码、改文件、跑测试
📅 更新于 2026-09-13
本文校对于 anomalyco/opencode @ v1.18.30(2026-09-09 发布)· 官方源:github.com/anomalyco/opencode
0030 秒速览:三条结论 + 按处境挑动作
读完这一篇,你能做出三个判断:你该用哪条安装路线、第一件事该敲什么、以及在哪一步必须先把闸门关上(因为 OpenCode 默认允许 AI 直接改你的文件,这一步不做,风险是你自己的)。
① 它不是补全插件,是"会自己动手"的代理——你说一句话,它去读文件、改文件、跑命令,而补全插件只在你敲字时猜下一个词(01)。② 它自己没有智能,智能来自你接的那个模型:OpenCode 是"外壳 + 手脚",模型是"大脑",所以你随时能换脑(09)。③ 默认权限是全开,edit 和 bash 都不问你就执行;进生产项目前先把 bash 设成 "ask"(10)。
按你的处境,直接做这一件
| 你现在的状态 | 直接做这个 | 为什么是它 |
|---|---|---|
| 还没装,只想先跑起来看看 | curl -fsSL https://opencode.ai/install | bash | 官方推荐的一条命令装法,跨平台(03) |
| macOS 上要"更新最快" | brew install anomalyco/tap/opencode | 官方 tap 比 Homebrew 官方的 formula 更新更勤(03) |
| 公司电脑装不了新东西 | npm install -g opencode-ai | Node 包管理器路线,不依赖系统权限(03) |
| 装完了,卡在"没模型可用" | 进 TUI 敲 /connect | 用哪个模型必须先在这里接上(04) |
| 第一次在这个项目里用 | 敲 /init | 它扫一遍仓库生成 AGENTS.md,之后的对话质量会明显不同(14) |
| 要让它改一个真实文件 | 先用 Tab 切到 Plan,看方案再切回 Build | Plan 模式把"改"这一步关掉了,只出方案(12) |
| 怕它执行破坏性命令 | 配 "permission": {"bash": "ask"} | 每次跑 shell 命令前都问你一次(10) |
| 它改错了,想回到改之前 | 敲 /undo | 撤销这一轮对话和它做的文件改动,前提是项目在 Git 里(08) |
| 对话变长、回答开始变笨 | 敲 /compact | 把长历史压成摘要,省上下文(08) |
| 要把它塞进脚本或 CI | opencode run "…" | 不进交互界面,跑一次就退出(06) |
| 想给队友看刚才这段对话 | 敲 /share | 生成公开链接(注意:任何拿到链接的人都看得到)(08) |
一条可执行的上手路径
跑 opencode --version。它应该吐出 1.18.30 这样的版本号——吐不出来说明 opencode 不在 PATH 里,见 03 的排错一行。
在任意目录敲 opencode 进界面,输入 /connect,选提供商、粘 API Key。新手可以直接用 OpenCode Zen(官方筛过的模型集合),见 04。
cd 进项目 → opencode → /init → 问一个只读问题 → Tab 切 Plan 出方案 → Tab 切回 Build 让它改 → /undo 试一次撤销。完整命令见 05。
在项目根目录建 opencode.json,把 bash 设成 "ask"、把 .env 类文件挡掉。这是唯一一条"不做就可能出事"的步骤,见 10。
说清"代理"和"补全"差在哪;独立选对安装方式并接上模型;用 Plan / Build 和 /undo 组成一条能反悔的改码流程;并且知道默认权限为什么必须先关小。本文讲的是OpenCode 这个工具本身——它怎么用、怎么配、坑在哪,不含"怎么写提示词让模型更聪明"那类模型侧话题。
01它到底会改我的文件吗
会。这是理解 OpenCode 的第一道分水岭,也是最多人误解的地方——所以先把这句话放在最前面:你说一句"把登录那块的 bug 修了",它真的会去改你的源码文件,不是给你一段建议让你自己粘。
- 工具——它手上有一组能真正操作你项目的动作:读文件、改文件、写新文件、按内容搜代码、按文件名找文件、跑 shell 命令、上网查资料。官方文档把这些叫内置工具(built-in tools),一共十几个。
- 代理——模型自己决定"现在该用哪个工具、传什么参数",然后看结果、再决定下一步。这个"决定—执行—看结果"的循环就是它和聊天窗口的全部区别。
- 权限——上面每个动作都能被配置成"直接做 / 问一下 / 不许做"。默认几乎是全放行的,这是 10 要专门讲的事。
你把它想成一个坐在你旁边、手放在键盘上的同事:你说的话是"任务",不是"提问"。所以"帮我看看这个函数"它能做,"帮我修好这个函数"它也能做——后者会真的落笔。日常工具里最接近的类比是"远程配对编程",不是"更聪明的搜索框"。
它不是哪几样东西
下面这几个"它不是",每一条都对应一个新手真的会踩的误解。看的时候顺便对一下你自己的预期。
| 你可能以为它是 | 实际上 | 误解为什么会产生 |
|---|---|---|
| 像 Copilot 那样在你敲字时补全 | 它是独立进程,占满一个终端窗口,你按回车它才动;它不看你光标在哪 | 两者都被叫"AI 编程工具",但补全猜的是"下一个词",它接的是"一整件事" |
| 一个"更强的大模型" | 它自己不含模型。所有推理都发生在你 /connect 接上的那个提供商那边 | 它启动后就要你选模型,很容易以为"选了模型 = 装了模型" |
| 一个聊天窗口 | 它默认能改文件、跑命令,而且默认不问你 | 界面长得像聊天,所以心理上会当成"随便问问没关系" |
| 一个 IDE 专属插件 | 主形态是终端程序;VS Code 那类集成是"你在 IDE 的集成终端里敲 opencode 时顺便装的扩展" | 官方确实有 IDE 扩展,容易反过来以为它依附于 IDE |
| 一个云服务,代码要上传 | 它是本地程序,在你自己的机器上跑,直接读写你磁盘上的文件 | 要填 API Key 这件事让它看起来像"连了个云服务" |
它和最常见的三个近邻差在哪
这一张表不是评测,是"该选哪个"的判断依据。下面每一行的差异点都能在你自己的使用场景里对照出来。
| 维度 | OpenCode | 补全型(GitHub Copilot 那类) | IDE 内嵌代理(Cursor 那类) |
|---|---|---|---|
| 主界面 | 终端 | 编辑器内联提示 | 编辑器侧栏 / 内嵌聊天 |
| 模型选择 | 75+ 提供商,可以换、可以接本地模型 | 基本跟着厂商走 | 主要跟着编辑器厂商给的清单 |
| 源码可见性 | 开源,MIT 许可 | 闭源 | 闭源 |
| 能不能 SSH 进服务器用 | 能——它就是个终端程序,装在哪就在哪跑 | 不能(要编辑器) | 不能(要图形界面) |
| 你要自己准备什么 | 一个模型提供商的 API Key(或用它的 Zen) | 一个订阅 | 一个订阅 |
| 最容易后悔的地方 | 默认可写文件可跑命令,得自己收紧权限 | 能用但不深入项目 | 换模型不自由 |
里面有什么:六个零件
你不需要背这张图,但看一眼能省掉后面一半的困惑——比如"为什么我要登录两次""为什么模型列表在另一个地方"。
| 零件 | 用大白话说是干什么的 | 你什么时候会直接碰到它 |
|---|---|---|
| TUI | "终端用户界面"的缩写,就是你会看到的那个全屏窗口:上面是消息流,下面是输入框 | 每次敲 opencode 进入的就是它 |
| Server | 本地跑着的一个服务,管会话状态、排队请求 | 你想从浏览器用(opencode web)或从别的终端接上去(opencode attach)时 |
| Agent(代理) | 一套"用哪个模型、能用哪些工具、提示词是什么"的组合,不是模型本身 | 按 Tab 切换主代理时,就是在换 Agent |
| Tools(工具) | 真正落手落脚的十几个动作:读、改、写、搜、跑命令…… | 你在配置里写 "permission": {"bash": "ask"} 时,改的就是某个工具 |
| Model(模型) | 真正做"思考"的那个大语言模型,跑在提供商那边 | 用 /models 换模型时 |
| Provider(提供商) | 模型的服务方,比如 Anthropic、OpenAI、OpenCode Zen,或者你本机的 Ollama | 用 /connect 添 API Key 时 |
因为 Server 是个独立进程,所以你在终端里敲 opencode serve 起一个后端,再用 opencode attach http://… 从另一台机器接上 TUI,是官方支持的用法——这意味着你可以把"跑模型请求的那台机器"和"你敲键盘的那台机器"分开。
02为什么不全用现成的工具
官方的仓库描述只有一句:The open source coding agent.(开源的编程代理)。它刻意把自己定位在"开源"和"代理"这两个字上,所以它的价值主张也得从这两处读。
它解决的是四类具体问题,不是"更好用"
| 你碰到的麻烦 | 换成 OpenCode 之后变成什么样 | 这条的代价(公平地说) |
|---|---|---|
| 想试新模型,但工具只认一家 | 换 model 一行配置,或 /models 选一下,就能切到另一家的模型 | 模型换来换去,同一个提示词的效果差异要你自己重新摸 |
| 想知道工具到底把什么发给了谁 | 源码公开(MIT 许可),可以自己读、自己改、自己编译 | 读源码是额外成本;自己改就得自己维护 |
| 主力开发在远程服务器上,本机只有终端 | 它就是终端程序,SSH 进去装一个就能用 | 没有图形界面时,图片类输入、可视化 diff 的体验会打折 |
| 成本想自己控制 | 可以按任务分模型:简单的活给小模型,难的活给大模型(配置里的 small_model 就是干这个的) | 分模型要花心思,配错了只是变慢或变贵,不会报错 |
OpenCode 本身确实免费、MIT 许可。但模型调用是要花钱的——除非你用本机跑的本地模型,或者用某个订阅额度(比如 GitHub Copilot 账号、ChatGPT Plus/Pro 账号的额度)。
最常见的翻车:装完接上一个大模型,一晚上让它读了整个 monorepo,第二天收到账单。
把 OpenCode 当成"一套免费的手脚",把模型当成"按量计费的大脑"。省钱的做法:日常问答用便宜模型,难任务临时切贵模型;长对话及时 /compact 或干脆 /new 开新的。
想完全不花钱:接本机的 Ollama / LM Studio 这类本地模型服务。
决定要不要用它,问自己三句话
- 我的活儿主要在终端里发生吗?如果开发、构建、测试全在终端,它的贴合度最高。
- 我愿意让一个程序直接改我的文件吗?如果不愿意,它还有一半价值——你可以只用 Plan 模式当"会读库的分析师"。
- 我手上有没有一个可用的模型入口?没有 API Key 也没有 Zen,就先去 04 把这一步解决,别急着装。
03装哪一条:六种安装方式怎么选
OpenCode 的安装方式多到有点烦,但它们的差别其实只有两点:谁帮你管更新、你的机器上已经有什么。先看表选一条,再往下抄命令。
先看这张表决定用哪个包管理器
| 方式 | 什么时候选它 | 自动更新 | 别在什么时候用 |
|---|---|---|---|
| 官方安装脚本 | 什么都不知道,只想先跑起来 | 会(启动时自动升级) | 公司机器不允许 curl | bash 时 |
| Homebrew(官方 tap) | macOS / Linux 上要最新版 | 用 brew upgrade 自己管 | 不想装 Homebrew 时 |
| npm / bun / pnpm / yarn | 机器上已经有 Node 生态,或者装不了系统级包 | 用包管理器自己管 | Node 版本太老时(先升级 Node) |
Arch Linux pacman | 你在 Arch 上,只要稳定版 | 随系统更新 | 想第一时间拿到新功能时(换 AUR 的 opencode-bin) |
| Windows Scoop / Chocolatey | 你在 Windows 上,不想开 WSL | 用包管理器自己管 | 想体验完整功能时——官方明确推荐 WSL |
| Docker / Mise | 要放进容器,或已经用 Mise 统一管工具版本 | 自己管 | 想直接用本机项目文件时(容器要自己挂目录) |
官方文档的第一句建议就是:Windows 上推荐用 WSL(Windows Subsystem for Linux,可以理解成"在 Windows 里开一个真正的 Linux")。理由是性能和功能兼容性更好。opencode web 的文档里还额外提了一句:Windows 上请从 WSL 里跑,别从 PowerShell 里跑,否则文件系统访问和终端集成会出问题。
把命令抄走
官方推荐的是安装脚本这一条:
curl -fsSL https://opencode.ai/install | bash
不想用脚本,就用包管理器。下面这些命令按平台分组,只抄你需要的那一行:
# Node 生态(npm / bun / pnpm / yarn,四选一)
npm install -g opencode-ai
bun install -g opencode-ai
pnpm install -g opencode-ai
yarn global add opencode-ai
# Homebrew(macOS / Linux,官方 tap,更新最快)
brew install anomalyco/tap/opencode
# Arch Linux:稳定版 / AUR 最新版
sudo pacman -S opencode
paru -S opencode-bin
# Windows:Scoop / Chocolatey / NPM
scoop install opencode
choco install opencode
npm install -g opencode-ai
# Mise(统一管版本的工具)
mise use -g github:anomalyco/opencode
# Docker(一次性跑,用完即走)
docker run -it --rm ghcr.io/anomalyco/opencode
跑 opencode --version,应该吐出 1.18.30 这样的版本号。如果提示找不到命令,说明安装目录不在 PATH 里:用 Homebrew 装的话通常是 /opt/homebrew/bin(Apple 芯片)或 /usr/local/bin;用 npm 装的话是 npm bin -g 给出的目录。把它加进 PATH 后重开终端。
装完想升级、想卸掉
# 升到最新版
opencode upgrade
# 升到指定版本(注意官方文档里的示例写的是 v0.1.48 这种老版本号,格式一致)
opencode upgrade v0.1.48
# 指定安装方式来升级:curl / npm / pnpm / bun / brew
opencode upgrade --method brew
# 卸载:先看不删什么(--dry-run 只打印要删哪些东西)
opencode uninstall --dry-run
# 真的卸:保留配置、保留会话数据与快照、不再问确认
opencode uninstall --keep-config --keep-data --force
它把东西都放在哪
出问题时你基本只需要翻这三个位置,所以值得记一下:
| 路径 | 里面是什么 | 什么时候要动它 |
|---|---|---|
~/.local/share/opencode/auth.json | 你 /connect 或 opencode auth login 存进去的 API Key | 想确认自己到底接了哪几家(或想手动清理)时 |
~/.config/opencode/opencode.json | 你的全局配置:默认模型、权限、提供商 | 想让"所有项目都默认把 bash 设成 ask"时 |
.opencode/(在你项目里) | 这个项目专属的东西:agents、commands、plugins、skills、tools、themes | 想给团队共享一个自定义命令时 |
桌面应用(Beta):不想用终端的人
官方除了终端界面,也提供了桌面版。可以从下载页取安装包,也可以走包管理器:
# macOS(Homebrew cask)
brew install --cask opencode-desktop
# Windows(Scoop,注意要先进 extras 这个 bucket)
scoop bucket add extras; scoop install extras/opencode-desktop
安装包的下载页是 opencode.ai/download,提供 macOS、Windows、Linux 三个平台。桌面版目前官方标着 Beta,所以本文讲的配置与命令都以终端版为基准,桌面版的界面差异请以它自己的文档为准。
让 /editor 和 /export 用你惯用的编辑器
这两个命令都会去读 EDITOR 这个环境变量。不设它的话,打开的可能是一个你不想用的默认编辑器。
# 设置编辑器
export EDITOR="code --wait" # VS Code
export EDITOR="cursor --wait" # Cursor
export EDITOR=nvim # Neovim
# 想永久生效,把上面那行加进 shell 配置:~/.bashrc、~/.zshrc 等
# Windows 上不一样:CMD 用 set,PowerShell 用 $env:
set EDITOR=notepad
set EDITOR=code --wait
$env:EDITOR = "notepad"
$env:EDITOR = "code --wait"
官方专门注明了这一条:像 VS Code 这类编辑器必须带 --wait。原因是它的启动方式默认是"把窗口打开就立刻返回"——不加 --wait,OpenCode 会以为你已经编辑完了。--wait 的作用就是让它一直阻塞到你关掉编辑器为止。可以在候选清单里挑:code、cursor、windsurf、nvim、vim、nano、notepad、subl。
04先接上一个模型
装完 OpenCode 之后,它还不能用——因为它自己没有模型。这一步做错或跳过,后面所有命令都会卡住。整个流程就三步。
三条命令之外没有别的坑
输入 opencode 回车,进入全屏界面。
/connect它会列出所有可选的提供商,让你选一个。
去提供商的网站上创建一个 Key,复制,粘到弹出来的输入框里,回车。
不想进界面也能做同样的事——命令行里也有一条:
# 交互式登录某个提供商(会问你要哪种登录方式、然后要 Key)
opencode auth login
# 直接指定提供商和登录方式,跳过两次选择
opencode auth login --provider anthropic --method api-key
# 看看现在已经接了哪些(这个命令不需要进界面)
opencode auth list
opencode auth ls
# 退掉某一家
opencode auth logout
提供商(provider)就是卖"模型的调用能力"的那家服务商。模型是货,提供商是店家,API Key 是你在这家店的会员卡。同一件"货"(比如某个 Claude 模型)可能在好几家店都有卖,价格和速度还不一样。这就是为什么 OpenCode 让你先选"店家"再选"货"——两件事。
不想折腾选模型,就用官方筛过的那一套
官方给新手的建议很直接:如果不知道选哪个模型,用 OpenCode Zen。它是 OpenCode 团队自己测过的模型集合,官方原话是"a curated list of models that have been tested and verified by the OpenCode team"(一份经过团队测试与验证的精选模型清单)。做法是:
# 1. 在 TUI 里敲 /connect
/connect
# 2. 在提供商列表里选 opencode
# 3. 浏览器打开下面这个地址,注册、绑支付方式、复制 API Key
# https://opencode.ai/auth
# 4. 把 Key 粘回 TUI
# 5. 接完敲 /models,官方推荐的模型就在列表里
/models
| 模型入口 | 适合谁 | 钱怎么算 | 别当什么用 |
|---|---|---|---|
| OpenCode Zen | 新手、不想研究模型差异的人 | 按请求计费,自己充额度 | 别当成"免费额度"——它是付费网关,只是帮你选好了模型 |
| 自己的提供商 Key | 已经有某家订阅或 Key 的人 | 照那家的价目表 | 别在一个项目里同时挂好几家 Key,换模型时容易选错 |
| GitHub Copilot 账号 | 已经有 Copilot 订阅的人 | 用订阅里的额度 | 别以为能拿到 Copilot 的全部功能——你只是借用它的模型额度 |
| ChatGPT Plus / Pro 账号 | 已经有 OpenAI 订阅的人 | 用订阅额度 | 同上,是额度不是功能 |
| 本地模型(Ollama / LM Studio) | 不想花钱、或代码不能出门的人 | 只花自己的电费和显卡 | 别指望本地小模型能扛住"重构整个模块"这种活 |
① Key 粘错了:复制时带了前后空格是高频问题,粘完看一遍开头结尾。② 环境变量和 auth.json 打架:如果你既设了 ANTHROPIC_API_KEY 这类环境变量、又在 /connect 里存了 Key,两边不一致时会让人怀疑人生。③ 企业代理:公司网络要走代理时,得在提供商配置里改 baseURL,见 13。
把"接了哪几家"和"有哪些模型"查清楚
# 命令行里列出所有提供商提供、且你已配置的模型(格式是 provider/model)
opencode models
# 只看看 anthropic 家的
opencode models anthropic
# 模型清单是缓存的,刚上新模型时手动刷一下
opencode models --refresh
# 想看带价格、上下文长度这类元数据的版本
opencode models --verbose
输出里的 provider/model-id 就是你在配置里该写的那串字。举个例子:如果你用的是 Zen,想看 GPT 5.1 Codex,写的就是 opencode/gpt-5.1-codex 这个格式——斜杠前面是店家,后面是具体那个模型。
05最小例子:十分钟走完一遍
下面这一段是一条从头到尾能照着敲的路径:从一个真实项目到让 AI 改一个真实文件,最后再安全地撤回来。全流程只需要一个终端窗口。
第一步:进入项目并启动
cd /path/to/your/project
opencode
第二步:让它先读懂这个项目
/init
/init 会扫描仓库里的重要文件,必要时问你一两个它自己答不上来的问题,然后生成(或就地改进)一份 AGENTS.md。它重点记的是:构建 / 检查 / 测试命令、命令的执行顺序、从文件名看不出来的架构、项目特有的约定。
没有 AGENTS.md 时,它每次都要靠猜你的项目怎么跑测试、代码放在哪。官方建议把它提交进 Git——这样团队里每个人、每次会话的起点都一样。如果你是从 Claude Code 迁过来的,它连 CLAUDE.md 也认(见 14)。
第三步:先问一个只读的问题
用 @ 引用文件是它最基本的动作。@ 会触发一次项目内的模糊搜索(你打几个字母它就把候选文件名列出来):
给我这个项目的代码结构做一个快速总结
How is authentication handled in @src/auth.ts
文件内容会被自动读进对话里。所以问"这个函数做什么"这类问题,它答得比你自己翻文件快。
第四步:让它跑一次命令给你看
消息以 ! 开头,就表示"这条不是给模型的话,是要执行的 shell 命令",输出会作为工具结果进入对话:
!git status
# 输出会被加入对话上下文,AI 能看到命令结果
更实用的写法是命令加问题一起来:
!git log --oneline -10
最近这 10 个提交里,哪个最可能跟"登录后偶发 500"有关?
第五步:要改代码,先切 Plan
按 Tab 或 Shift+Tab 在主代理之间切换(默认的两个是 Build 和 Plan,右下角会显示当前是哪个)。Plan 模式下"改文件"和"跑命令"默认都要问你,所以它只会给你方案:
# 1. 按 Tab 切到 Plan 模式
当用户删除笔记时,我们需要在数据库中标记为已删除。
然后创建一个页面显示最近删除的笔记,
用户可以从这个页面恢复或永久删除笔记。
# 2. AI 给出实现方案
# 3. 觉得可以,按 Tab 切回 Build 模式
看起来不错,开始实现吧。
第六步:撤回来看看
这一步强烈建议现在就试一次——知道"能撤"之后,你才敢放手让它改:
/undo
# 它会撤掉最近这条消息、以及它引起的所有文件和代码改动
# 然后把你刚才那句话重新显示出来,方便你改一改再发一次
/redo
# 反悔了:重做刚才撤销掉的那一批改动
官方原文:Internally, this uses Git to manage the file changes. So your project needs to be a Git repository.(内部通过 Git 管理文件改动,所以你的项目必须是一个 Git 仓库。)目录里没有 .git,这两个命令就不工作。好消息是它用的是自己的内部快照机制,不需要你先把改动提交过。
第七步:长对话了把它压一压
/compact
# 把当前这一段长历史压成摘要,接着聊(别名 /summarize)
/new
# 或者干脆开一段新会话(别名 /clear)——任务换了一个主题时更推荐这个
完整的一套照抄版
如果你只想复制一整段,从装到撤回是这样的:
# 装(一次)
curl -fsSL https://opencode.ai/install | bash
opencode --version
# 接模型(一次):进界面敲 /connect 并粘 Key
opencode
# 在项目里干活
cd /path/to/your/project
opencode
# 在 TUI 里依次敲:
# /init 生成 AGENTS.md
# 这个项目的入口在哪? 先问只读问题
# !git status 让它看看工作区状态
# Tab 切到 Plan 出方案
# Tab 切回 Build 执行
# /undo 不满意就撤回
# /exit 走人(别名 /quit、/q)
把设计稿、报错截图直接拖到终端窗口里,它会当成图片输入读进去。官方文档里就是用这一招收"照着这张设计图做这个页面"这种需求(原文:Drag and drop images into the terminal to add them to the prompt.)。Plan 模式迭代方案时特别有用:参考这张图改一下布局 [图片]。
06终端界面长什么样
OpenCode 的主界面就是一个「终端里的应用」。它复用了你终端的一部分能力(滚动、选中),又接管了一部分(键盘输入)。知道哪块归谁管,能省掉一次「我打了个字怎么没反应」的困惑。
两种启动方式
# 在当前目录启动
opencode
# 指定项目目录启动(不 cd 也能直接开)
opencode /path/to/project
四块区域,各管一件事
| 位置 | 上面有什么 | 你什么时候会看它 |
|---|---|---|
| 顶部 | 当前模型、当前代理名、这一段会话的信息 | 怀疑「它是不是换了个小模型在答我」时 |
| 中部 | 对话消息流:你说的话、它的回复、它调用的每一次工具及其结果 | 想看它到底跑了什么命令、改了哪些文件时 |
| 底部 | 输入框和快捷键提示 | 每次打字都在这里 |
| 右下角 | 当前代理模式的指示器 | 按 Tab 之后确认「切成功了吗」时 |
输入框里几个键,先认清它们
| 键 | 作用 | 新手最容易混的地方 |
|---|---|---|
Enter | 发送 | 想换行时别按它——按它就直接发出去了 |
Shift+Enter(或 Ctrl+Enter / Alt+Enter / Ctrl+J) | 在消息里换行 | 不同终端支持不一样,四个里总有一个能用 |
Tab / Shift+Tab | 在主代理之间正向 / 反向切换 | 它切的是代理不是模型;换模型用 /models |
Ctrl+P | 打开命令面板,能搜到所有可用命令和设置项 | 记不住斜杠命令名时靠它 |
Esc | 打断它正在做的事 | 它不是「退出程序」,是「停下当前这一轮」 |
Ctrl+X | 默认的前缀键,单独按没反应,要再按一个字母 | Ctrl+X 然后 u 才是撤销 |
Ctrl+Z | 把界面挂起、暂时回到 shell | 想跑一条本机命令又不想退出会话时用 |
有些终端程序会把某个组合键设成「开场白」:先按它,松手,再按一个字母,两个键合起来才算一条快捷键。OpenCode 默认的前缀键是 Ctrl+X,官方文档里写成 <leader>。所以你看到 <leader>u,意思就是「按 Ctrl+X,松手,再按 u」。默认它等你 2000 毫秒(2 秒)看你还按不按第二个键,这个时间可以配。
把界面调成你顺手的样子
界面相关的东西放在另一个文件里:tui.json(或带注释的 tui.jsonc)。它和管运行行为的 opencode.json 是分开的两份,可以放全局的 ~/.config/opencode/tui.json,也可以放项目里。
{
"$schema": "https://opencode.ai/tui.json",
"theme": "opencode",
"leader_timeout": 2000,
"keybinds": {
"leader": "ctrl+x",
"command_list": "ctrl+p"
},
"scroll_speed": 3,
"scroll_acceleration": {
"enabled": false
},
"diff_style": "auto",
"cursor": {
"style": "block",
"blinking": true
},
"mouse": true
}
| 配置项 | 它改的是什么 | 什么时候该动它 · 别踩哪里 |
|---|---|---|
theme | 配色主题的名字 | 终端背景不是纯黑、默认主题刺眼时改;旧文档里写过 TUI 配置能放在 opencode.json 的 tui 键下,现在那份已标记为过时并会自动迁移,新写法是独立文件 |
keybinds | 键位映射,会和内置默认值合并 | 习惯的前缀键不是 Ctrl+X 时改;只写你要改的那几条就够 |
leader_timeout | 按完前缀键后等多久算「你放弃了」,单位毫秒,默认 2000 | 手慢、常按不出快捷键时调大 |
scroll_speed | 滚动多快,默认 3,可写小数(最小 0.001) | 觉得一次滚太多时调小 |
scroll_acceleration.enabled | 开启「滚得越快、滚得越远」的手感 | 开着它的时候,scroll_speed 会被忽略——两个别同时配 |
diff_style | "auto" 跟终端宽度自适应,"stacked" 强制单列 | 代码改动对比在窄窗口被压扁时改 stacked |
cursor | 输入框光标的样子与是否闪烁 | 光标和背景同色、看不见时改 |
mouse | 是否接管鼠标,默认 true | 想用终端原生的「选中复制、滚轮翻页」时设 false |
attention.enabled | 桌面通知与提示音,默认关 | 任务跑完时你在看别的窗口,想被叫回来才开 |
① 按 Ctrl+P 打开命令面板,搜 "username" 可以把消息前面那个用户名显示关掉——这个开关会自己记住,下次进来还是关的。② 界面的配色直接在 tui.json 里改 theme;想先看看有哪些主题,用命令面板搜主题相关的项,比手写配置快。
三样东西跑在 TUI 之外,但你要知道它们在
# 浏览器界面:起一个本地服务并自动打开浏览器
opencode web
opencode web --port 4096
# 让局域网里别人也能连(注意:同时必须设访问密码,否则等于裸奔)
opencode web --hostname 0.0.0.0
OPENCODE_SERVER_PASSWORD=secret opencode web
# 只起后端、不给界面(给别的客户端用)
opencode serve
# 一台机器起后端,另一台机器把 TUI 接上去
opencode web --port 4096 --hostname 0.0.0.0
opencode attach http://10.20.30.40:4096
官方在 opencode web 的文档里明确写了:If OPENCODE_SERVER_PASSWORD is not set, the server will be unsecured.(没设密码,服务就是不设防的。)本机自己用没问题,但一旦绑到 0.0.0.0(意思是「这台机器的所有网卡都能连」),同一个 Wi-Fi 下的人就能操作你的项目。正确姿势是 OPENCODE_SERVER_PASSWORD=你的密码 opencode web --hostname 0.0.0.0,用户名默认是 opencode。想换用户名就设 OPENCODE_SERVER_USERNAME。
VS Code 里的扩展是怎么装上的
这里要纠正一个很常见的误解:你不需要先去装扩展。官方给的做法是——打开 VS Code,打开集成终端,在里面敲 opencode,扩展会自己装上。
# 在 VS Code / Cursor / Windsurf / VSCodium 的集成终端里敲:
opencode
# 之后扩展自动安装。装不上时按下面的三步查。
① 确认你是在集成终端里敲的 opencode,不是系统终端;② 确认 IDE 的命令行工具装了——VS Code 是 code、Cursor 是 cursor、Windsurf 是 windsurf、VSCodium 是 codium;没装的话在 IDE 里按 Cmd+Shift+P(Mac)或 Ctrl+Shift+P,搜 "Shell Command: Install 'code' command in PATH";③ 确认 IDE 有安装扩展的权限。
07第一次进项目先敲什么
上面那十分钟已经覆盖了主干。这一节补的是"能跑通之后,第一周真正会反复用的东西"——尤其是当你一次想干几件事的时候。
并行干活:同一份代码,几条会话同时开
每次 /new 都是一段独立会话。这带来一个很实用的用法:让一段会话去研究、另一段去实现,互不干扰。
# 会话 1:让它研究"认证是怎么做的"
# 会话 2:让它改一个完全无关的小 bug
# 用 /sessions 在它们之间来回切(别名 /resume、/continue)
/sessions
# 或者直接用命令行起一段新会话
opencode --continue # 接着上一次
opencode --session <session-id> # 接指定的那一段
opencode --fork # 在续接时岔出一支,不污染原来那条线
不进交互界面也能问一句
这是它最被低估的一条能力——opencode run 把整个代理塞进一条命令里,跑完就退出:
opencode run "Explain how closures work in JavaScript"
# 挂到一个已经跑着的后端上,省掉每次冷启动 MCP 服务器的等待
opencode serve
opencode run --attach http://localhost:4096 "Explain async/await in JavaScript"
# 让它把结果按 JSON 吐出来,方便脚本解析
opencode run --format json "列出这个仓库里所有的 TODO 注释"
# 自动批准没有被显式禁止的权限(CI 里用,见 08)
opencode run --auto "Refactor this module"
对团队:分享、导出、交接
# 生成一个链接并复制到剪贴板
/share
# 取消分享
/unshare
# 导出成 Markdown,用你的默认编辑器打开
/export
# 命令行里:导出某段会话为 JSON(不给 id 会让你选)
opencode export
opencode export --sanitize # 抹掉敏感内容再导出
# 把别人给你的会话 JSON 导进来(也支持分享链接)
opencode import session.json
opencode import https://opncd.ai/s/abc123
官方原文写得很直白:Shared conversations are publicly accessible to anyone with the link.(分享出去的对话,任何拿到链接的人都能看。)分享时它会把你这段对话历史同步到官方服务器上,并给你一个 opncd.ai/s/<id> 形式的公开地址。所以分享前先确认这段对话里没有密钥、没有客户数据、没有内部地址。三种分享模式的开关在 13。
训练自己的肌肉记忆:五条就够
第一遍:只读
只用 @文件 提问和 !命令,不让它改任何东西。目标是建立"它读得懂我的项目"的信任感。
第二遍:让它只改一个文件
挑一个你有把握的小改动,走 Plan → Build。做完立刻 !git diff 看一眼它到底改了什么——这个习惯能省掉很多惊吓。
第三遍:引入 /undo
故意让它改一个你不想要的方向,然后用 /undo 拉回来。目的是把"改错不可怕"变成本能。
第四遍:配置权限
建 opencode.json,把 bash 设成 "ask"。之后正常干活一天,感受一下"问得太勤"还是"刚刚好"。
第五遍:写一个自己的命令
把你每天重复说的一段话(比如"跑测试、只看失败的、给修复建议")做成 .opencode/commands/test.md,以后敲 /test 就行。模板见 13。
08斜杠命令与快捷键
在界面里敲 /,候选列表就出来了。这些叫斜杠命令——它们是"你控制这个工具"的开关,不是发给模型的提示词。下面按"你什么时候需要"分两张表。
十七个斜杠命令,一次列全
第三列是这套清单里哪一条会咬人,赶时间的话只看它。
| 命令 | 它做什么 | 什么时候用 · 别踩哪里 |
|---|---|---|
/connect | 添加模型提供商、填 API Key | 装完第一件事;接错了模型会一直报「没有可用模型」 |
/init | 引导式创建或更新 AGENTS.md | 第一次进项目必做;已有 AGENTS.md 时是就地改进,不是覆盖 |
/models | 列出可用模型 | 换模型;它切的是模型,和 Tab 切的代理是两件事 |
/new | 开一段新会话,别名 /clear | 换主题时用;快捷键 ctrl+x n |
/sessions | 列出并切换会话,别名 /resume、/continue | 同时在跑几条线时回来找;快捷键 ctrl+x l |
/undo | 撤销最近一条消息,以及它引起的全部文件改动 | 改错了撤回来。项目必须在 Git 仓库里;快捷键 ctrl+x u |
/redo | 重做上一次撤销掉的东西 | 只有 /undo 之后才可用;快捷键 ctrl+x r |
/compact | 把当前会话压成摘要,别名 /summarize | 对话太长、回答开始变笨时;快捷键 ctrl+x c |
/share | 分享当前会话,生成链接并复制到剪贴板 | 给队友看。任何拿到链接的人都能看,先确认没有密钥 |
/unshare | 取消分享 | 发现分享错了时的补救 |
/export | 把会话导成 Markdown,用你的默认编辑器打开 | 要归档、要贴进文档时;快捷键 ctrl+x x,用的是 EDITOR 环境变量 |
/editor | 打开外部编辑器来写这条消息 | 要写一大段需求、终端里敲着难受时;快捷键 ctrl+x e |
/themes | 列出可用主题 | 换配色;快捷键 ctrl+x t |
/thinking | 显示 / 隐藏模型思考过程那一段 | 只是显示开关,不会让模型开始或停止推理;要真的切推理档位用 ctrl+t |
/details | 切换工具执行详情(它跑了什么、输出什么) | 排查「它刚才到底干了什么」时 |
/help | 弹出帮助对话框 | 记不住时 |
/exit | 退出,别名 /quit、/q | 走人;快捷键 ctrl+x q |
官方原文:Internally, this uses Git to manage the file changes. So your project needs to be a Git repository.(内部用 Git 管文件改动,所以项目必须是 Git 仓库。)没有 .git 目录,/undo 和 /redo 都不工作。可以连续执行多次 /undo 往回退多步;每退一步,会把文件恢复到那条消息之前的状态。
没有斜杠版本的那些快捷键
下面这些只在键盘上。默认的前缀键是 Ctrl+X(表里写成 <leader>)。
| 快捷键 | 作用 | 记住它的理由 |
|---|---|---|
Tab / Shift+Tab | 正向 / 反向轮换主代理 | Plan 和 Build 的切换就靠它,这是最该形成肌肉记忆的一个 |
Ctrl+P | 打开命令面板 | 所有命令和设置项都能搜到,等于一张活的速查表 |
<leader>u / <leader>r | 撤销 / 重做 | 和 /undo、/redo 等价,手不用离开键盘 |
<leader>e | 打开外部编辑器写消息 | 长需求的效率差很多 |
<leader>x | 导出会话 | 归档时用 |
<leader>n / <leader>l | 新会话 / 会话列表 | 管多条并行会话 |
<leader>t / <leader>m / <leader>a | 主题列表 / 模型列表 / 代理列表 | 三张「选一个」的清单,比记斜杠命令更统一 |
<leader>b / <leader>s | 开关侧边栏 / 查看状态 | 窄窗口下腾地方 |
<leader>y | 复制最后一条消息 | 要把它的回答贴到别处时 |
<leader>g | 看会话时间线 | 长会话里回跳到某个节点 |
Ctrl+T | 轮换模型变体(官方叫 variant,用来选推理强度这类档位) | 和 /thinking 不是一回事:它改的是「用哪个档位」,不是「显不显示」 |
F2 / Shift+F2 | 在最近用过的模型之间正 / 反向切 | 两个模型来回对比时最省事 |
<leader>↓ | 进入第一个子会话 | 子代理干的活单独开会话,这是进去看它干了什么的路 |
← / → / ↑(在子会话里) | 子会话之间前后切换 / 回到父会话 | 看完子代理的活要回到主线 |
Esc | 打断当前这一轮 | 发现它走错方向时立刻停,不是退出程序 |
Ctrl+C / Ctrl+V / Ctrl+Z | 清空输入框 / 粘贴 / 把界面挂起回 shell | 注意 Ctrl+C 在输入框里是「清空」不是「退出」 |
上面这些名字(messages_undo、session_list、model_list……)就是 tui.json 里 keybinds 的键名。想改成什么就写什么,比如 {"keybinds": {"leader": "ctrl+space"}}。没写的那些还按默认值走,所以不用把几十条全抄一遍。
把你每天重复说的话做成一条命令
斜杠命令列表里还可以有你自己的东西。做法有两种,先看 Markdown 文件那种——它最直观:
# 建这个文件(项目级)或 ~/.config/opencode/commands/test.md(全局级)
# .opencode/commands/test.md
---
description: Run tests with coverage
agent: build
model: anthropic/claude-3-5-sonnet-20241022
---
Run the full test suite with coverage report and show any failures.
Focus on the failing tests and suggest fixes.
文件名 test.md 就成了命令名 test,敲 /test 就发出去。不想用文件,也可以全写在配置里:
{
"$schema": "https://opencode.ai/config.json",
"command": {
"test": {
"template": "Run the full test suite with coverage report and show any failures.\nFocus on the failing tests and suggest fixes.",
"description": "Run tests with coverage",
"agent": "build",
"model": "anthropic/claude-haiku-4-5"
},
"component": {
"template": "Create a new React component named $ARGUMENTS with TypeScript support.\nInclude proper typing and basic structure.",
"description": "Create a new component"
}
}
}
自定义命令里能塞三种动态内容
这是把"重复劳动"真正消掉的地方。三种写法可以混着用。
# 1. 参数占位:$ARGUMENTS 收全部参数,$1 $2 $3 收第几个
# .opencode/commands/component.md
---
description: Create a new component
---
Create a new React component named $ARGUMENTS with TypeScript support.
Include proper typing and basic structure.
# 用起来:/component Button → $ARGUMENTS 被换成 Button
# .opencode/commands/create-file.md
---
description: Create a new file with content
---
Create a file named $1 in the directory $2
with the following content: $3
# 用起来:/create-file config.json src "{ \"key\": \"value\" }"
# → $1 = config.json,$2 = src,$3 = { "key": "value" }
# 2. 把命令输出塞进提示词:用 !`命令` 这个写法
# .opencode/commands/analyze-coverage.md
---
description: Analyze test coverage
---
Here are the current test results:
!`npm test`
Based on these results, suggest improvements to increase coverage.
# .opencode/commands/review-changes.md
---
description: Review recent changes
---
Recent git commits:
!`git log --oneline -10`
Review these changes and suggest any improvements.
# 3. 引用文件:还是用 @
# .opencode/commands/review-component.md
---
description: Review component
---
Review the component in @src/components/Button.tsx.
Check for performance issues and suggest improvements.
命令在你项目的根目录执行,输出会直接变成提示词的一部分。规律很简单:要变化的字用 $ARGUMENTS;要先跑一遍命令看当下状态用 !`…`;要固定看某几个文件用 @路径。这三样凑起来,「每天开工先看一遍 CI 状态并总结」这种活就能压成一条 /开工。
09模型、提供商和 Agent 是三件事
这是最容易混成一团的一组词,而且混了之后所有配置都写不对。一句话先分开:提供商是店家,模型是货,Agent 是"用哪件货、配哪些工具、说什么话"的一个组合。
用一张收货单理解这三层
| 概念 | 大白话 | 在配置里长什么样 | 换它意味着什么 |
|---|---|---|---|
| 提供商(provider) | 卖模型调用服务的那家店,比如 Anthropic、OpenAI、OpenCode Zen,或者你本机的 Ollama | "provider": {"anthropic": {…}} | 换的是「去谁家买」,通常要重新填 Key、计费也跟着换 |
| 模型(model) | 货本身,比如某个 Claude、某个 GPT | "model": "anthropic/claude-sonnet-4-5"——斜杠前是店家,后面是货 | 换的是「用哪个脑子」,能力和价格都变 |
| Agent(代理) | 一套预设:用哪个模型、允许哪些工具、系统提示词是什么 | "agent": {"build": {"model": "…", "permission": {…}}} | 换的是「以什么身份、带着什么权限干活」 |
官方对 small_model 的解释正好说明这三层是可以拆开配的:它专门给"起标题"这类小事配一个便宜模型,默认行为是"能从你的提供商那里找到更便宜的模型就用它,找不到就退回主模型"。
# 把主模型和"干杂活的模型"分开
{
"$schema": "https://opencode.ai/config.json",
"model": "anthropic/claude-sonnet-4-5",
"small_model": "anthropic/claude-haiku-4-5"
}
同一家店还能调什么
调完店、调完货,还有一层"这家店怎么联系"的参数。它们在 provider.<名字>.options 里:
| 参数 | 它管什么 | 默认值 / 什么时候动它 |
|---|---|---|
timeout | 整个请求最多等多久(毫秒) | 默认 300000(5 分钟);设成 false 表示不限时 |
headerTimeout | 等响应头最多等多久;一旦头到了它就不再计时,不限制后面的流式内容 | 默认 300000;服务商「先不回话、慢慢吐字」时别指望它兜底 |
chunkTimeout | 两个流式片段之间最多隔多久,超了就中断请求 | 默认 300000;网络抖就断的场合可以调大 |
setCacheKey | 给指定的提供商固定设一个缓存键 | 默认不带;配合某些提供商的提示词缓存时用 |
baseURL | 换掉这家店的接口地址 | 走公司代理、或用兼容接口的自建服务时用 |
apiKey | 这一家的密钥 | 一般用 {env:…} 引环境变量,别写死在文件里 |
blacklist / whitelist | 从模型选择列表里藏掉 / 只保留某些模型 | 店里有一堆你永远不用的模型、每次翻列表很烦时用 |
# 一家店的常见调法:超时、缓存键、换接口地址、藏掉不用的模型
{
"$schema": "https://opencode.ai/config.json",
"provider": {
"anthropic": {
"options": {
"timeout": 600000,
"chunkTimeout": 30000,
"setCacheKey": true,
"apiKey": "{env:ANTHROPIC_API_KEY}"
},
"blacklist": ["claude-opus-4-20250514"]
}
}
}
不想让某个提供商被自动加载
有时候你机器上有某个 Key,但你就是不想让这次的会话用它——比如公司要求只走指定的店。两个配置项管这件事:
# 明确禁掉几家(优先级更高)
{
"disabled_providers": ["openai", "gemini"]
}
# 或者反过来:只允许这几家,其余全不加载
{
"enabled_providers": ["anthropic", "openai"]
}
官方写明:disabled_providers 的优先级高于 enabled_providers,而且这是为了向后兼容特意保留的行为。所以同一家店同时出现在两个列表里时,它是被禁用的。被禁用的店有三重后果:即使环境变量里有 Key 也不加载、即使 /connect 里存过 Key 也不加载、它的模型也不会出现在模型选择列表里。
不确定该用哪个模型时
官方给的答案就是 OpenCode Zen,理由写在它自己的文档里:There are a large number of models out there but only a few of these models work well as coding agents.(模型很多,但真正适合当编程代理的只有少数。)而且同一件货在不同店家那里的表现也不一样,所以官方做了三件事:测了一批模型并与模型团队沟通怎么跑最好、和几家提供商一起确保服务方式正确、最后对"模型 + 提供商"这个组合做实测打分,得出一份可推荐的清单。
三种情况值得换:① 它反复理解错你的项目约定——换一个更强的;② 它在做机械劳动(改名、加日志)烧钱太快——换一个便宜的;③ 它不支持你要的推理档位——换 Ctrl+T 切变体,或换模型。反过来,"它答得不好"里只有一部分是模型问题,还有很大一部分是上下文没给够(见 17 的成对示例)。
10别让它乱改:权限与自动模式
这是全文最该认真读的一节。官方在配置文档里写得很明白:By default, opencode allows all operations without requiring explicit approval.(默认情况下,OpenCode 允许所有操作,不需要明确批准。)也就是说——它一装上就是"什么都不问"的状态。不改它,你的第一周就是拿真实项目试手。
不是"它只会读",而是"它读写都能做、命令也能跑,而且不问你"。默认只有两类事情会来问你:碰到项目目录以外的路径(external_directory),以及同一个工具用完全相同的输入连着调了 3 次(doom_loop,官方把它叫「代理看起来卡住了」的救援提示)。读文件默认也是放行的,唯一的例外是 .env 系列文件默认被挡。
三种动作:放行、问你、禁止
| 取值 | 它的意思 | 什么时候该用哪一档 |
|---|---|---|
"allow" | 不用批准,直接执行 | 只读类、你完全信任的操作(比如 grep、glob);或者在你自己的玩具仓库里图省事 |
"ask" | 每次执行前弹出来问你 | 生产项目里 bash 的推荐档位;改动别人的代码、跑可能影响环境的命令时 |
"deny" | 这个工具直接不可用 | 明确知道某些事不该由 AI 做时,比如让它禁止 git push、禁止改某个目录 |
先抄一份"能睡好觉"的配置
如果你只想抄一段就走,抄这个。它的思路是:读放开、写和跑命令要问、几类危险命令直接禁、密钥文件不许读。
{
"$schema": "https://opencode.ai/config.json",
"permission": {
"*": "ask",
"read": "allow",
"grep": "allow",
"glob": "allow",
"edit": "ask",
"bash": {
"*": "ask",
"git status *": "allow",
"npm test *": "allow",
"git push *": "deny",
"rm -rf *": "deny"
}
}
}
官方原文:Rules are evaluated by pattern match, with the last matching rule winning.(按模式匹配逐条判断,最后一条匹配的规则生效。)所以上面那段里,*: ask 写在最前面、具体规则写在后面——顺序颠倒过来,整段就失效。官方文档给的常见做法也是这一条:先放 catch-all 的 "*",再放更具体的。
哪里能写、写了能不能被覆盖
| 位置 | 给谁用 | 优先级 |
|---|---|---|
全局 ~/.config/opencode/opencode.json | 你个人的默认习惯 | 低;会被项目配置盖掉 |
项目根目录 opencode.json | 这个团队的约定,可以提交进 Git | 高;盖掉全局 |
Agent 自己的 permission | 只约束某一个代理 | 最高;和全局合并后,Agent 的规则优先 |
| 管理员托管配置 | 公司强制下发、用户改不了 | 最高一层,连项目配置也盖不了它 |
可配的权限清单:十四个键
第三列告诉你"这个键为什么值得单独配"——多数人只需要动其中三四个。
| 权限键 | 它挡的是哪些动作 | 为什么值得配 · 别当什么用 |
|---|---|---|
read | 读文件(按文件路径匹配) | 默认放行,但 .env 默认被挡;别拿它当"防泄漏"的万能闸——真正机密的东西不该放在它读得到的目录里 |
edit | 所有文件修改,覆盖 edit、write、apply_patch 三个工具 | 最该配的一个;分成 "ask" 是「改前过目」,"deny" 是「只让它出方案」 |
bash | 跑 shell 命令(按解析出来的命令匹配,比如 git status --porcelain) | 风险最高的一项;至少设 "ask",并对 git push *、rm -rf * 这类上 "deny" |
glob | 按模式找文件(匹配的是那个模式字符串) | 纯读操作,一般放行;仓库特别大时才考虑限制 |
grep | 搜文件内容(匹配的是正则串) | 同上,放行;想禁止它读某个目录的内容时用路径级规则 |
task | 启动子代理(匹配的是子代理的类型名) | 想控制"它能派出哪些小代理"时用;"deny" 会让这个子代理从工具描述里彻底消失,模型就不会去试 |
skill | 加载技能文件(匹配技能名) | 你装了很多技能、只想让某几个能被自动取用时的开关 |
lsp | 跑语言服务的查询 | 目前不支持细粒度规则,只能整体开关;实验性功能 |
question | 它在执行途中向你提问 | 关掉它 = 让它别打断你,但代价是它只能自己猜 |
webfetch | 抓取某个网址(匹配 URL) | 代码不能出门的场合,把它设 "deny" 比设 "ask" 更省心 |
websearch | 联网搜索(匹配查询词) | 同上;另外这个工具本身还有前置条件,见 12 的工具表 |
external_directory | 任何读写项目目录之外路径的动作 | 默认就是 "ask";这是防"它跑到你主目录乱翻"的主要一道闸 |
doom_loop | 同一个工具调用、完全相同的输入,连着重复 3 次 | 默认 "ask";它不是安全闸,是"代理卡住了"的救援提示 |
todowrite | 维护任务清单的工具(覆盖 todowrite、todoread) | 子代理默认不能碰这两个工具,需要的话手动开 |
同一件事,在"全局/项目配置"里叫 todowrite,在 Agent 的 Markdown 配置文件里官方表格写作 todowrite / todoread 一组。另外 Agent 文档里还多列出一个 list 键(管"列目录"那个工具),它在权限总表里没有单列。这类命名不一致是真实存在的,写配置时以你那份文档页的表格为准,别凭印象拼。
细粒度规则:让"读能进去、改不能"
权限的值除了写一个动作,还可以写一个"模式 → 动作"的对象。中间支持的匹配规则很简单:* 匹配任意多个字符,? 匹配正好一个字符,其他字符按字面匹配。另外 ~ 和 $HOME 可以在开头代表你的主目录。
# 一个典型场景:只允许读某个目录,不允许改它
{
"$schema": "https://opencode.ai/config.json",
"permission": {
"external_directory": {
"~/projects/personal/**": "allow"
},
"edit": {
"~/projects/personal/**": "deny"
}
}
}
官方专门澄清过这一点:external_directory 里的 ~ 展开只影响你怎么写这个模式,不会把那个路径算成当前工作区。所以"项目目录以外的路径"仍然必须靠 external_directory 显式允许,别指望写个 ~ 就全通了。另外,被允许的目录会继承当前工作区的默认值——因为 read 默认放行,所以被允许的目录里的读也是放行的,除非你再单独加规则把它挡住。
"ask" 弹出来之后,三个选项分别是什么
| 你的选择 | 它批准的范围 | 什么时候点它 |
|---|---|---|
once | 只批这一次 | 拿不准这条命令的后果时;多问几次总比误批好 |
always | 批准"和这次匹配的同一类模式"——比如 bash 通常会给一个安全前缀,像 git status* | 确认它是重复性的安全操作;注意这个批准只在当前这一段会话里有效 |
reject | 拒绝这一次 | 它想跑的正是你不允许的事;拒绝之后它通常会换个做法或来问你 |
自动模式:批量放行,但禁掉的仍然禁掉
有些场合你不想被问(比如在 CI 里跑、或者你自己盯着屏幕),可以开自动模式:
# 启动时开
opencode --auto
# 非交互的一次性任务也支持
opencode run --auto "Refactor this module"
官方对它的边界说得很清楚:Explicit "deny" rules are still enforced. Auto mode only changes requests that would otherwise ask for approval.(显式写 "deny" 的规则照样执行;自动模式只改那些「本来会来问你」的请求。)在界面里也能切:按 Ctrl+P 打开命令面板,搜 "auto-approve" 就能开关;开着的时候,输入框旁边会有一个淡淡的 auto 提示。
如果你只打算改一行配置,就改这一行:在项目根目录的 opencode.json 里写 {"permission": {"bash": "ask"}}。它把最高风险的那一项从"默默执行"变成"每次过目",而读写文件仍然顺畅——性价比最高的一道闸。
三段可以直接抄的细粒度配置
前面讲的是规则,这里是三段真实可用的写法。它们的差别在于"你想拦到多细"。
# ① 全局两档:改要问、跑命令要问(最小可用)
{
"permission": {
"edit": "ask",
"bash": "ask"
}
}
# ② 把 bash 拆开:默认问,几条读类命令放行,几条危险命令禁止
{
"permission": {
"bash": {
"*": "ask",
"git status": "allow",
"git status *": "allow",
"npm test": "allow",
"npm test *": "allow",
"git push": "deny",
"git push *": "deny"
}
}
}
因为匹配的是解析后带参数的完整命令。官方在权限页的提示里写得很清楚:"grep *" 能放行 grep pattern file.txt,而只写 "grep" 会把带参数的那种挡在外面;git status 这种"不带参数能用"的,一旦带了参数就需要显式放行。同时写"不带参数"和"带参数"两条,是这里唯一不会漏的写法。
# ③ 按代理分开配:全局不许改,但 build 这个主代理例外
{
"permission": { "edit": "deny" },
"agent": {
"build": {
"permission": { "edit": "allow" }
}
}
}
# ④ 同一个思路,写在一个 Markdown 代理文件里(只读审查代理)
# ~/.config/opencode/agents/review.md
---
description: Code review without edits
mode: subagent
permission:
edit: deny
bash:
"*": ask
"git diff": allow
"git log*": allow
"grep *": allow
webfetch: deny
---
Only analyze code and suggest changes.
最后这段值得多看一眼:它只放行了三条读类命令(git diff、以 git log 开头的、以及 grep 带参数的),其余 bash 一律要问、抓网页直接禁止。一个"只做审查、绝不改代码"的代理,配出来就是这么几行。
11@ 和 ! 什么时候用
消息框里有两个符号不是普通文字:@ 表示"去把项目里的文件喂给我看",! 表示"这条先当命令跑掉"。这两个符号决定了它到底知不知道你在说什么,所以值得单独讲清。
@ 引用文件:它替你做了"复制粘贴文件内容"这件事
你在消息里打 @,它会在这个项目里做一次模糊搜索——你打几个字母,候选文件名就列出来了,选中即可。官方文档的说法是:The content of the file is added to the conversation automatically.(文件内容会被自动加进对话。)
这个函数的逻辑不太对,帮我看看 @src/utils/format.ts
# OpenCode 会自动读取文件内容并加入对话上下文
# 更具体的问法:把两个文件一起给它
我们需要给 /settings 路由加认证。
参考 @packages/functions/src/notes.ts 里的写法,
在 @packages/functions/src/settings.ts 里实现同样的逻辑。
! 前缀:让它的眼睛看到"现场"
以 ! 开头的整条消息会被当成 shell 命令执行,输出作为工具结果进入对话。官方的说法是 The output of the command is added to the conversation as a tool result——注意"工具结果"这个词,意味着它和它自己跑命令看到的东西是同一种东西。
!ls -la
!git log --oneline -10
!git diff HEAD~3
因为它的"自己去看"要花好几轮:先想用什么命令、再等你批准、再看输出。而你直接 !git diff HEAD~3 把它要看的东西一次性放到它面前,省掉的是往返次数(也省了 token)。最实用的组合是「命令 + 问题」写在一条消息里。
引用别名:把"隔壁仓库"也变成 @ 得出来的东西
项目里常有这种情况:真正要参考的文档在另一个仓库。references 就是为这个场景准备的——配一次别名,之后 @别名 即可。
# 完整写法:本地目录用 path,Git 仓库用 repository + 可选 branch
{
"$schema": "https://opencode.ai/config.json",
"references": {
"docs": {
"path": "../product-docs",
"description": "Use for product behavior and documentation conventions"
},
"sdk": {
"repository": "anomalyco/opencode-sdk-js",
"branch": "main",
"description": "Use for JavaScript SDK implementation details"
}
}
}
# 简写:不需要分支和描述时,直接写路径或仓库
{
"references": {
"docs": "../docs",
"effect": "Effect-TS/effect"
}
}
# 用起来:@别名 挂上整个根,@别名/ 继续往里搜文件
对比我们的配置和 @docs/README.md
对比一下我们的实现和 @sdk/src/client.ts
| 字段 | 本地目录 | Git 仓库 | 它是干什么的 |
|---|---|---|---|
path | 可以 | 不行 | 指向一个本地目录;可以是相对配置文件的位置、绝对路径、或 ~/ 开头 |
repository | 不行 | 可以 | 接受 Git URL、host/path 形式,或 GitHub 的 owner/repo 简写;它会把这个仓库拉到本地缓存里再用 |
branch | 不行 | 可以 | 指定分支或 ref;不写就用仓库的默认分支 |
description | 可以 | 可以 | 告诉模型"什么时候该用这个引用";只有带描述的引用才会被主动告诉模型 |
hidden | 可以 | 可以 | 从 @ 自动补全里藏掉;它只影响补全,带描述的隐藏引用仍然会进模型上下文 |
官方写明了:别名不能为空,也不能包含 /、空白、反引号、逗号。想成"文件夹名"就行——因为 @别名/ 后面还要接路径,别名里再出现斜杠就分不清边界了。
引用和权限是什么关系
这一条挺关键,因为它解释了"为什么我配了 @docs 就能读隔壁目录,而别的目录还是要问":
- 配成引用的目录,OpenCode 会自动把它从"项目外路径"那道闸里放行;
- 但普通工具权限照样管用——官方举的例子是:一个不允许改文件的代理,不会因为某个目录被配成了引用就获得改动它的权限;
- Git 类型引用的拉取是异步的,刚配好时可能还在 clone,过一会儿才可用。
# 把引用藏起来,不污染 @ 补全列表(但仍然希望模型知道它存在 → 保留 description)
{
"references": {
"internal": {
"path": "../internal",
"description": "Use for internal implementation details",
"hidden": true
}
}
}
它的作用是"让模型知道该去哪找、并让 @ 能补全出来",不是"给它一片法外之地"。真要约束,还得回到 permission 那一套规则上写(见 10)。
12主代理与子代理
Agent(代理)这个词在 OpenCode 里指的不是模型,而是一套预设:用哪个模型、允许用哪些工具、系统提示词是什么、在界面上什么颜色。官方把内置的代理分两类:主代理(直接和你对话的)和子代理(被主代理派出去干专项活的)。
两类的差别只有一句话
| 类型 | 谁在跟它说话 | 你怎么启动它 | 它的 description 写给谁看 |
|---|---|---|---|
| 主代理(primary) | 你。它是你正在对话的那个 | Tab / Shift+Tab 轮换,或用配置里的 default_agent 指定默认那个 | 写给你看,帮你判断该切到它 |
| 子代理(subagent) | 主代理。你一般不直接和它聊 | 主代理自己按任务派;你也可以在消息里 @名字 直接点它 | 写给模型看——它靠这段描述判断"这个任务该不该交给它" |
主代理决定要不要派子代理时,看的就是子代理的 description。所以你自定义一个子代理时,描述要写成"什么时候用它"而不是"它是什么"。一个可用的写法是:Reviews code for best practices and potential issues(官方示例原话),而不是"代码审查代理"。
五个内置代理,各管一段
除了这五个,还有三个"隐藏的系统代理"(compaction、title、summary),它们负责压缩上下文、起会话标题、写会话摘要,自动跑,你在界面上选不到。
| 代理 | 类型 | 它能做什么 | 什么时候用它 |
|---|---|---|---|
| Build | 主代理(默认) | 所有工具都开着:能读、能改、能跑命令 | 日常开发、写代码、修 bug——它是你 80% 时间的默认状态 |
| Plan | 主代理 | 受限代理:官方原话是「以下全部默认设为 ask」——所有文件编辑(写入、打补丁、编辑)与所有 bash 命令 | 分析代码、出方案、评审建议。这是"先别动手"的开关 |
| General | 子代理 | 通用子代理,工具几乎全开(只差待办清单那个),所以它也能改文件 | 研究复杂问题、执行多步骤任务;官方建议用它来并行跑几块工作 |
| Explore | 子代理 | 只读,不能改文件。按模式找文件、按关键词搜代码、回答关于代码库的问题 | "这个项目里 X 在哪、怎么实现的"这类快速摸路 |
| Scout | 子代理 | 只读,看外部文档和依赖库:能把依赖仓库 clone 到它的缓存里、查库的源码、对照本地实现和上游 | 怀疑"是不是库自己有问题"、或要照着上游写法改时 |
默认配置里 Plan 会把 edit 和 bash 都设成「要问你」。这不是"它不能改",而是"它每次想改都会先停下来问你"——所以你在 Plan 模式下说"实现它",它会先给你方案而不是直接动手。真正的用法是:Plan 里把方案讨论清楚 → 按 Tab 切到 Build → 一句"看起来不错,开始实现吧"。这一来一回省下的是"它按错的理解改了 200 行"。
怎么调用子代理
三条路,按你可能用到的顺序排:
# 1. 手动点名:在消息里 @ 它
@general help me search for this function
# 2. 自动:主代理按子代理的 description 自己决定派谁
# 3. 看它干的活:子代理会开自己的"子会话"
# 进入第一个子会话 <leader>+↓(默认 ctrl+x 然后方向键下)
# 在子会话之间轮换 →(右箭头)
# 反向轮换 ←(左箭头)
# 回到父会话 ↑(上箭头)
子代理的价值在于:它读的一大堆文件不占你主会话的上下文——它在自己的会话里读,只把结论带回来。但代价是:它看到的东西和你当前对话的上下文不共享,所以给它的指令必须自洽、不能靠"你懂的"。另外官方默认只允许一层子代理(子代理不能再派子代理),想放宽要动 subagent_depth(见 13)。
它手上到底有哪些工具
这是"权限表"的另一面。上一节讲的是"怎么拦",这一张讲的是"它本来能干什么"。
| 工具 | 它干什么 | 由哪个权限键管 | 特别要注意的 |
|---|---|---|---|
bash | 在你项目环境里执行 shell 命令 | bash | 风险最高;匹配的是解析后的命令(比如 git status --porcelain),所以带参数的命令要写 "git status *" 这种带星号的规则 |
edit | 用"精确替换某段字符串"的方式改现有文件 | edit | 官方说它是"LLM 改代码的主要方式";它只替换完全匹配的文本,所以改造上下文不唯一的代码时会失败 |
write | 创建新文件、或覆盖已有文件 | edit | 注意它会覆盖同名文件;它没有独立的权限键,和 edit、apply_patch 共用 |
read | 读文件内容 | read | 支持只读某几行(大文件时有用);.env 系列默认被挡 |
grep | 用正则搜文件内容 | grep | 底层是 ripgrep,默认遵守 .gitignore——这既省时间也是个坑(见下) |
glob | 按 **/*.ts 这类模式找文件 | glob | 返回的文件按修改时间排序 |
lsp | 问语言服务:跳定义、找引用、悬停信息、调用层级 | lsp | 实验性,只有打开实验开关才出现;能省掉大量"全文翻找" |
apply_patch | 应用补丁文件 | edit | 它能一次做增删改移四种操作,路径信息写在补丁的标记行里(比如 *** Update File: src/a.ts) |
skill | 加载一个技能文件(SKILL.md)并把内容带进对话 | skill | 装了技能包时它才会出现在工具列表里 |
todowrite | 维护一份任务清单,跟踪多步进度 | todowrite | 子代理默认没有这个工具,需要手动开 |
webfetch | 抓取某个网址的内容 | webfetch | 用途是"取回已知地址的内容",不是"找资料" |
websearch | 联网搜索 | websearch | 有前置条件:只用 OpenCode / OpenCode Go 这两家提供商时自带,否则要设 OPENCODE_ENABLE_EXA 或 OPENCODE_ENABLE_PARALLEL;不需要额外的 API Key |
question | 在执行途中反过来问你问题 | question | 一次可以问多个,每个带选项,你也可以自己敲答案 |
# 联网搜索的两条开启方式(默认不满足前置条件时不出现这个工具)
OPENCODE_ENABLE_EXA=1 opencode
OPENCODE_ENABLE_PARALLEL=1 opencode
# LSP 工具是实验性的,要显式打开
OPENCODE_EXPERIMENTAL_LSP_TOOL=true opencode
官方写明:grep 和 glob 底层用 ripgrep,而 ripgrep 默认遵守 .gitignore——.gitignore 里列掉的文件和目录,它搜不到也列不出。想让它看进去,在项目根目录建一个 .ignore 文件,把要放开的路径以 ! 开头写进去:
!node_modules/
!dist/
!build/
这条也解释了一个常见困惑:"为什么它说找不到那个文件?"——先去确认那个文件是不是被 .gitignore 排掉了。
自己造一个代理:两种写法
内置的五个不够用时,可以加自己的。JSON 那种适合"顺手改一下内置代理",Markdown 那种适合"长期维护一个专用代理"。
# 写法一:写在配置里(opencode.json)
{
"$schema": "https://opencode.ai/config.json",
"agent": {
"build": {
"mode": "primary",
"model": "anthropic/claude-sonnet-4-20250514",
"prompt": "{file:./prompts/build.txt}",
"permission": {
"edit": "allow",
"bash": "allow"
}
},
"plan": {
"mode": "primary",
"model": "anthropic/claude-haiku-4-20250514",
"permission": {
"edit": "deny",
"bash": "deny"
}
},
"code-reviewer": {
"description": "Reviews code for best practices and potential issues",
"mode": "subagent",
"model": "anthropic/claude-sonnet-4-20250514",
"prompt": "你是一个代码审查专家。关注安全性、性能和可维护性。",
"permission": {
"edit": "deny"
}
}
}
}
# 写法二:一个 Markdown 文件。放项目级或全局级:
# .opencode/agents/review.md
# ~/.config/opencode/agents/review.md
---
description: Reviews code for quality and best practices
mode: subagent
model: anthropic/claude-sonnet-4-20250514
temperature: 0.1
permission:
edit: deny
bash: deny
---
You are in code review mode. Focus on:
- Code quality and best practices
- Potential bugs and edge cases
- Performance implications
- Security considerations
Provide constructive feedback without making direct changes.
这是同一个机制做出来的第二个代理——一个安全审计用的子代理。它和上面那个只差在提示词和文件位置:
# .opencode/agents/security-auditor.md
---
description: 代码安全审计
mode: subagent
permission:
edit: deny
---
你是一个安全专家。关注:
- 输入验证漏洞
- 认证和授权缺陷
- 数据暴露风险
- 依赖漏洞
文件的名字就是代理名:review.md 就创建了一个叫 review 的代理;security-auditor.md 就是 security-auditor。两者都被设成 mode: subagent、edit: deny,所以它们能看、能分析,但改不了你的代码——这正是"审查类代理"该有的形状。
一个代理能配哪些选项
| 选项 | 它管什么 | 取值与默认 | 什么时候真的需要它 |
|---|---|---|---|
description | 这个代理是干什么、什么时候用 | 必填 | 子代理必填;它是主代理决定"派不派它"的唯一依据 |
mode | 这个代理是主代理还是子代理 | primary / subagent / all;不写默认 all | 想让一个代理既能 Tab 切到、又能被 @ 点名时用 all |
model | 这个代理用哪个模型 | 不写时:主代理用全局配置那个,子代理沿用调用它的主代理的模型 | 想给"规划"配便宜模型、"实现"配强模型时 |
prompt | 系统提示词;写 {file:./prompts/x.txt} 可以指向一个文件 | 路径相对配置文件所在目录 | 提示词很长、想单独维护时 |
temperature | 回答的随机程度 | 常用区间 0.0–1.0;不写时按模型自己的默认(多数模型是 0,Qwen 系列常见是 0.55) | 0.0–0.2 适合分析规划;0.6–1.0 适合头脑风暴 |
steps | 这个代理最多能"自己动手"多少轮,到顶就强制只输出文字 | 不写则不限,一直做到模型自己停或你打断 | 想把成本封顶时用;旧名字 maxSteps 已废弃,用 steps |
permission | 这个代理的权限,和全局合并,同键时以它为准 | 见 10 的十四个键 | 做只读代理、或做"能改但禁止 push"的代理时 |
hidden | 从 @ 自动补全里藏掉 | 只对 mode: subagent 生效 | 内部专用代理,不想让它出现在补全里、但仍希望模型能通过工具派它 |
disable | 把这个代理关掉 | true / false | 不想用内置的某个代理、又不想删配置时 |
color | 它在界面上的颜色 | 十六进制色值如 #FF5733,或主题色名 primary / accent / success 等 | 代理多起来之后靠颜色分辨 |
permission.task | 这个代理允许派哪些子代理 | 模式对象;"deny" 会让那个子代理从工具描述里彻底消失 | 做一个"只能派自己那几个下属"的编排代理时 |
# 用命令行交互式造一个新代理(想量产时把四个参数都给全,就变成非交互)
opencode agent create
opencode agent list
# 四个参数都给全时,它不再问你任何问题
opencode agent create --path .opencode/agents --description "审查 SQL 注入风险" --mode subagent --permissions read,grep,glob
官方写得很清楚:你没允许的都会被否掉。可用清单是 bash, read, edit, glob, grep, webfetch, task, todowrite, websearch, lsp, skill,默认值是 all(全给)。也就是说——如果你为了"安全"而写了一个空的 --permissions,得到的确实是最小权限;但如果你随手照抄了"默认 all",那你造出来的是一个权限全开的代理。这一步值得停下来看一眼参数。
你可能在旧教程里看到 "tools": {"write": false} 这种写法。官方已经把它标为废弃(deprecated),推荐改用 permission 字段,因为它能表达更细的规则。旧写法目前仍然兼容:在那套语义里,true 等价于 {"*": "allow"},false 等价于 {"*": "deny"},并且支持 mymcp_* 这种通配符来一次控制一个 MCP 服务器下的所有工具。
13配置文件与优先级
OpenCode 的配置是多份合并的,不是"一份覆盖另一份"。官方原话:Configuration files are merged together, not replaced.(配置文件是合到一起的,不是替换关系。)明白这一点,才能解释"我明明改了全局配置怎么不生效"——因为项目里那份把它盖掉了。
八层优先级,从低到高
后面盖前面。你日常只会碰到中间三层,但知道有托管层存在,能解释"为什么这条配置我改不动"。
| 顺序 | 来源 | 它在哪 | 你什么时候会用它 |
|---|---|---|---|
| 1(最低) | 远程配置 | 组织在 .well-known/opencode 提供的默认值 | 你加入了某家提供商的账号时它自动生效,你一般不用管 |
| 2 | 全局配置 | ~/.config/opencode/opencode.json | 你的个人习惯:默认模型、默认权限 |
| 3 | 自定义路径配置 | OPENCODE_CONFIG 指定的文件 | 想在"全局"和"项目"之间插一层时(比如按客户切换) |
| 4 | 项目配置 | 项目根目录的 opencode.json | 团队约定,可以提交进 Git |
| 5 | .opencode 目录内容 | .opencode/ 下的 agents、commands、plugins 等 | 放自定义代理、自定义命令时 |
| 6 | 内联配置 | OPENCODE_CONFIG_CONTENT 里直接写的 JSON | 临时试一段配置,不想落文件时 |
| 7 | 管理员托管文件 | macOS /Library/Application Support/opencode/、Linux /etc/opencode/、Windows %ProgramData%\opencode | 公司强制下发的配置,需要管理员权限才能写,你改不了 |
| 8(最高) | macOS 托管偏好 | 通过 MDM 下发的 .mobileconfig(偏好域 ai.opencode.managed) | 企业环境;官方说它不可被用户覆盖 |
官方举的例子最能说明问题:全局配置里写了 autoupdate: true,项目配置里写了 model: "anthropic/claude-sonnet-4-5"——最后生效的配置里两条都在。只有同一个键冲突的时候,后面的才盖前面的。所以你的全局配置不会因为项目里放了个小配置就整体失效。
放配置的三条经验规则
- 项目根目录的
opencode.json是最该用的一个:官方说明它启动时会从当前目录往上找到最近的 Git 目录;所以放在仓库根最稳,也建议提交进 Git。 .opencode和~/.config/opencode里的子目录用复数名:agents/、commands/、modes/、plugins/、skills/、tools/、themes/。单数名(比如agent/)也仍然能用,是为向后兼容留的——但新写就写复数。- 想让配置跟着人走而不是跟着仓库走:放全局;总在没有 .git 的目录里手动跑时要知道,"往上找最近的 Git 目录"这一步会一路找到家目录为止。
常用配置项:一份能直接用的样例
下面这份把最常动的几项都放进去了,注释写在右侧。抄的时候记得删掉注释——或者干脆把文件命名成 opencode.jsonc,那种格式允许注释。
{
"$schema": "https://opencode.ai/config.json",
"model": "anthropic/claude-sonnet-4-5",
"small_model": "anthropic/claude-haiku-4-5",
"autoupdate": true,
"formatter": true,
"lsp": true,
"permission": {
"edit": "allow",
"bash": "ask"
},
"instructions": ["CONTRIBUTING.md", "docs/guidelines.md"]
}
其余常用项,按你的需求点菜
| 配置项 | 它管什么 | 取值 / 默认 | 什么时候动它 |
|---|---|---|---|
autoupdate | 启动时自动下载更新 | true / false / "notify" | 想收通知但不想自动升级时写 "notify"。用包管理器装的它不管——那种升级交给 brew / npm |
formatter | 代码格式化 | 省略 = 关闭;true = 开;对象 = 开内置再配覆盖或自定义 | 它改完文件后自动跑格式化,团队有统一格式时开 |
lsp | 语言服务 | 省略 = 关闭;true = 开;对象 = 开内置再覆盖 | 类型化语言项目里开着,能让它"跳定义"而不是翻全文 |
instructions | 额外的指令文件 | 路径或 glob 数组,如 ["CONTRIBUTING.md", ".cursor/rules/*.md"] | 规则分散在好几个文件里时,比全塞进 AGENTS.md 好维护 |
shell | 用哪个 shell 跑命令 | 短名或绝对路径;不写就按系统自动选 | Windows 上想固定用 pwsh 时 |
share | 分享模式 | "manual"(默认)/ "auto" / "disabled" | 代码敏感就别用 "auto";想彻底关掉写 "disabled" |
snapshot | 文件改动快照,/undo 与 /redo 的底座 | 默认开 | 大仓库、子模块多时它可能拖慢索引并占磁盘;关掉就不能用界面回滚 |
compaction | 上下文压缩策略 | auto 默认 true、prune 默认 false、reserved 是预留的 token 缓冲 | 对话经常撞上限、或想省 token 时调 |
watcher.ignore | 文件监听忽略哪些路径 | glob 数组 | 仓库里有大量生成物、监听吵得慌时 |
default_agent | 不指定时默认用哪个代理 | 必须是主代理;写错会退回 build 并警告 | 你就是想默认进 Plan 模式时 |
subagent_depth | 子代理还能不能再派子代理 | 默认 1;0 表示完全禁止派子代理,2 表示允许多一层 | 想编排"多层小代理"时;或者反过来想彻底关掉这个能力 |
plugin | 从 npm 加载插件 | 包名数组,如 ["opencode-helicone-session", "@my-org/custom-plugin"] | 想扩功能时;插件文件也可以直接放 .opencode/plugins/ |
disabled_providers / enabled_providers | 哪些提供商不加载 / 只加载哪些 | 提供商 ID 数组 | 见 09;注意前者优先级更高 |
attachment.image | 图片输入的预处理上限(把图片统一缩放成一致的规格再发出去) | 默认超过 2000×2000 像素或 5242880 字节的 base64 就缩;auto_resize 设 false 则改为直接拒绝 | 截图模糊、看不清小字时,把这几个上限调大 |
两个占位符,让配置不写死秘密
官方支持两种替换:{env:变量名} 取环境变量,{file:路径} 取文件内容。
{
"$schema": "https://opencode.ai/config.json",
"model": "{env:OPENCODE_MODEL}",
"provider": {
"anthropic": {
"options": {
"apiKey": "{env:ANTHROPIC_API_KEY}"
}
}
}
}
# 从文件里读密钥,避免把密钥写进配置文件
{
"instructions": ["./custom-instructions.md"],
"provider": {
"openai": {
"options": {
"apiKey": "{file:~/.secrets/openai-key}"
}
}
}
}
官方写明:If the environment variable is not set, it will be replaced with an empty string.(环境变量没设时,它会被替换成空字符串。)这意味着你少设一个变量,不会看到"配置错误",只会看到"认证失败"或"模型不存在"——排查时先往这里想。另外 {file:…} 的路径可以是相对于配置文件的位置、绝对路径、或 ~/ 开头。
常被问到的几个环境变量
| 变量 | 类型 | 它干什么 |
|---|---|---|
OPENCODE_CONFIG | 字符串 | 指定配置文件路径(插在全局与项目之间那一层) |
OPENCODE_CONFIG_DIR | 字符串 | 指定一个自定义配置目录,会在全局配置和 .opencode 之后加载,所以能盖掉它们 |
OPENCODE_CONFIG_CONTENT | 字符串 | 直接内联一段 JSON 配置,运行时覆盖 |
OPENCODE_TUI_CONFIG | 字符串 | 指定自定义的 TUI 配置路径 |
OPENCODE_PERMISSION | 字符串 | 内联的权限配置 JSON |
OPENCODE_DISABLE_AUTOUPDATE | 布尔 | 关掉自动更新检查 |
OPENCODE_DISABLE_AUTOCOMPACT | 布尔 | 关掉自动上下文压缩 |
OPENCODE_ENABLE_EXA / OPENCODE_ENABLE_PARALLEL | 布尔 | 打开对应的联网搜索工具(见 12) |
OPENCODE_SERVER_PASSWORD / OPENCODE_SERVER_USERNAME | 字符串 | 给 serve / web 加基础认证;用户名默认 opencode |
OPENCODE_GIT_BASH_PATH | 字符串 | Windows 上指定 Git Bash 的位置 |
OPENCODE_DISABLE_CLAUDE_CODE | 布尔 | 完全关掉对 .claude 目录(提示词 + 技能)的读取 |
OPENCODE_DISABLE_MODELS_FETCH | 布尔 | 不从远端拉模型清单(离线或内网时有用) |
接一个 MCP 服务器:让模型多出几只手
MCP(Model Context Protocol,模型上下文协议)是"给模型接外部工具"的一套标准接口。接上之后,那个服务器提供的工具会和内置工具一样出现在模型手里。
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"mcp_everything": {
"type": "local",
"command": ["npx", "-y", "@modelcontextprotocol/server-everything"]
},
"my-local-mcp-server": {
"type": "local",
"command": ["npx", "-y", "my-mcp-command"],
"enabled": true,
"environment": {
"MY_ENV_VAR": "my_env_var_value"
}
},
"my-remote-mcp": {
"type": "remote",
"url": "https://my-mcp-server.com",
"enabled": true,
"headers": {
"Authorization": "Bearer MY_API_KEY"
}
}
}
}
# 接上之后,在提示里直接点名那个服务器的工具
use the mcp_everything tool to add the number 3 and 4
| 字段 | 本地(local) | 远程(remote) | 它是干什么的 |
|---|---|---|---|
type | 必填,写 "local" | 必填,写 "remote" | 选哪种连接方式。旧教程里写的 "stdio" 已经不是现在的取值了 |
command | 必填 | — | 怎么启动这个 MCP 服务器;是个数组,比如 ["npx", "-y", "包名"] |
url | — | 必填 | 远程 MCP 服务器的地址 |
headers | — | 可选 | 随请求带的请求头,用来放鉴权信息 |
environment | 可选 | — | 启动这个服务器时设的环境变量 |
cwd | 可选 | — | 服务器进程的工作目录;相对路径从工作区算起 |
enabled | 可选 | 可选 | 开关。组织下发的远程配置里常有 "enabled": false,你在自己配置里写 true 就能打开 |
timeout | 可选 | 可选 | 拉取这个服务器的工具清单时最多等多久,默认 5000 毫秒 |
oauth | — | 可选 | 需要 OAuth(一种"跳到你浏览器里点同意、再换一张临时通行证"的授权方式)时的配置;不写也会自动处理——它收到 401(服务器说"你没登录")就会自己发起授权流程 |
# 命令行管 MCP:加、列、授权、退授权、排查
opencode mcp add
opencode mcp list
opencode mcp auth my-oauth-server
opencode mcp logout my-oauth-server
opencode mcp debug my-oauth-server
# 一个接数据库的例子(官方文档里的另一种写法:type 用 local,command 是数组)
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"database": {
"type": "local",
"command": ["npx", "-y", "@modelcontextprotocol/server-postgres", "postgresql://localhost/mydb"]
}
}
}
# 想临时停掉某一个而不从配置里删:把它的 enabled 设成 false
{
"mcp": {
"jira": {
"type": "remote",
"url": "https://jira.example.com/mcp",
"enabled": false
}
}
}
# 需要手填客户端凭据时(服务器方给你的)
{
"mcp": {
"my-oauth-server": {
"type": "remote",
"url": "https://mcp.example.com/mcp",
"oauth": {
"clientId": "{env:MY_MCP_CLIENT_ID}",
"clientSecret": "{env:MY_MCP_CLIENT_SECRET}",
"scope": "tools:read tools:execute"
}
}
}
}
官方专门写了一节注意事项:When you use an MCP server, it adds to the context. This can quickly add up if you have a lot of tools.(用 MCP 服务器会增加上下文,工具一多就涨得很快。)它还点了名:GitHub 那个 MCP 服务器特别占 token,很容易把上下文顶满。够用就行,别一次挂一堆。想临时停掉某个服务器,把它的 enabled 设成 false,不用从配置里删。
更多现成的 MCP 服务器怎么配,官方有一个单独页面:opencode.ai/docs/mcp-servers。想少装东西试一下,可以用官方文档里那个只提供一个加法工具的测试服务器 @modelcontextprotocol/server-everything。
后面这几段是配置里能写、但你在别处不一定看得到的组合,按需取用。
# 1. 服务器选项(serve / web 共用)
{
"server": {
"port": 4096,
"hostname": "0.0.0.0",
"mdns": true,
"mdnsDomain": "myproject.local",
"cors": ["http://localhost:5173"]
}
}
# 2. 自定义格式化器(保留内置的,只加/改某一个)
{
"formatter": {
"prettier": { "disabled": true },
"custom-prettier": {
"command": ["npx", "prettier", "--write", "$FILE"],
"environment": { "NODE_ENV": "development" },
"extensions": [".js", ".ts", ".jsx", ".tsx"]
}
}
}
# 3. 只允许某些提供商使用(企业里常见的白名单写法)
{
"experimental": {
"policies": [
{ "effect": "deny", "action": "provider.use", "resource": "openai" }
]
}
}
experimental 这一层官方标了警告:它不稳定,可能不加通知就改或删掉。企业场景用它,个人场景别依赖它。
14AGENTS.md:写给 AI 的项目说明书
如果说 10 管的是"它能做什么",这一节管的是"它知不知道该怎么做"。答案是:靠项目根目录那个叫 AGENTS.md 的文件。官方对它的定位是:里面的指令会被放进模型的上下文里,用来把它的行为调成适合你这个项目的样子。
/init 到底做了什么
不用手写。敲一条命令它就替你建:
/init
官方对它的描述值得逐句读,因为它决定了你该期待什么:它会扫一遍仓库里的重要文件、在代码本身答不上来时问你几个有针对性问题,然后创建或更新 AGENTS.md。它重点记的是这五类东西:
- 构建 / 检查 / 测试的命令;
- 命令的执行顺序、以及什么时候需要"只跑一小块验证";
- 从文件名看不出真实意图的架构与仓库结构;
- 项目特有的约定、装环境的怪癖、运维上的坑;
- 对已有指令来源(比如 Cursor 或 Copilot 的规则文件)的引用。
官方写明:If you already have an AGENTS.md, /init will improve it in place instead of blindly replacing it.(如果你已经有 AGENTS.md,/init 会就地改进它,而不是盲目替换。)所以「手写一份 + 后面用 /init 补」是可行路线,但每次跑完还是 !git diff 看一眼它改了什么更稳。
一份可以照抄结构的 AGENTS.md
下面是官方文档里的示例。它的写法很克制——只写"命令 + 约定 + 结构",不写状态、不写历史。这个取舍值得学:这份文件每次会话都会被读进去,写多了等于每次都在多花你的上下文预算。
# SST v3 Monorepo Project
This is an SST v3 monorepo with TypeScript. The project uses bun workspaces for package management.
## Project Structure
- `packages/` - Contains all workspace packages (functions, core, web, etc.)
- `infra/` - Infrastructure definitions split by service (storage.ts, api.ts, web.ts)
- `sst.config.ts` - Main SST configuration with dynamic imports
## Code Standards
- Use TypeScript with strict mode enabled
- Shared code goes in `packages/core/` with proper exports configuration
- Functions go in `packages/functions/`
- Infrastructure should be split into logical files in `infra/`
## Monorepo Conventions
- Import shared modules using workspace names: `@my-app/core/example`
放哪一份、哪一份说了算
| 位置 | 作用范围 | 优先级 | 什么时候用它 |
|---|---|---|---|
项目根目录的 AGENTS.md | 只在这个目录及其子目录生效 | 本项目内最高 | 团队约定,应该提交进 Git |
~/.config/opencode/AGENTS.md | 你所有 OpenCode 会话 | 低于项目里的那一份 | 你自己的个人习惯(官方特别提醒:这个不会提交、不共享给团队) |
项目里的 CLAUDE.md | 只在这个目录及其子目录生效 | 只在没有 AGENTS.md 时才用 | 你从 Claude Code 迁过来,还不想改文件名时 |
~/.claude/CLAUDE.md | 你所有 OpenCode 会话 | 只在没有 ~/.config/opencode/AGENTS.md 时才用 | 同上,全局版 |
官方给的查找顺序是:先从当前目录往上找本地文件(AGENTS.md、CLAUDE.md),然后是 ~/.config/opencode/AGENTS.md,最后是 ~/.claude/CLAUDE.md。每一类里第一个命中的赢——所以你同时有 AGENTS.md 和 CLAUDE.md 时,只有前者会被用。这条规则解释了那个经典困惑:"我明明改了 CLAUDE.md 怎么没用?"
不想要 Claude Code 那套兼容,就关掉
# 完全关掉对 .claude 的读取(提示词 + 技能)
export OPENCODE_DISABLE_CLAUDE_CODE=1
# 只关 ~/.claude/CLAUDE.md 这份提示词
export OPENCODE_DISABLE_CLAUDE_CODE_PROMPT=1
# 只关 .claude/skills 里的技能
export OPENCODE_DISABLE_CLAUDE_CODE_SKILLS=1
规则文件不止一份时,用 instructions 汇总
很多项目已经有自己的规范文档(CONTRIBUTING.md、.cursor/rules/……)。与其把它们复制进 AGENTS.md,不如让 OpenCode 直接去读:
{
"instructions": ["CONTRIBUTING.md", "docs/guidelines.md", ".cursor/rules/*.md"]
}
它和 Plan 模式不是一回事
这两个是新手最容易混的一对,因为它们都"让 AI 更懂规矩",但管的是完全不同的两件事:
| AGENTS.md(+ instructions) | Plan 模式(permission) | |
|---|---|---|
| 它回答的问题 | 它知不知道该怎么做:项目用什么命令、代码放在哪、有什么约定 | 它允不允许动手:能不能改文件、能不能跑命令 |
| 生效方式 | 内容进模型上下文,影响它的判断 | 在工具被调用时拦一道,影响它能不能做 |
| 配错了的表现 | 它会用错命令、改错地方,但会真去改 | 它一直来问你,或者你根本没发现它在改 |
| 什么时候该动 | 新项目、新团队、目录结构变了 | 进生产项目前、要它只出方案时 |
官方示例通篇只讲"结构 + 命令 + 约定",没有一句是"当前正在做什么"。原因很直接:这份文件每次会话都会被读一遍,写进去的历史备注、临时状态、过期待办,全都是每次都要重新读一遍的噪音。要放"今天在做什么",用任务清单工具或直接在对话里说。
15六个真实场景
前面讲的是零件。这一节把零件拼成六件真事,每一件都给一段能直接抄进去的话。注意每段的写法有共同点:都给上下文(@文件)、都给验收标准("加测试""只看失败的"),最后一件还先自己把现场贴进去(!命令)。
先按你的任务挑一个
| 你要干的事 | 用哪个模式 | 关键写法 | 对应场景 |
|---|---|---|---|
| 刚接手一个陌生代码库 | Plan 或只读提问 | 先让它给结构总结,再逐块问 @目录/ | 场景一 |
| 加一个新功能 | Plan → Build | 先说清需求、要它列改动文件,确认后再实现 | 场景二 |
| 修一个具体 bug | Plan | 引用具体文件 + 描述可复现的现象,并要它加测试 | 场景三 |
| 大规模重构 | Plan 先试跑 | 先在一个目录上试,确认它理解了模式再铺开 | 场景四 |
| 补测试 | Build | 明确覆盖范围(正常流程 + 错误场景) | 场景五 |
| 审查最近的改动 | Plan | 自己先 !git diff 把改动摆出来,再让它审 | 场景六 |
| 只想问个技术问题 | 不启动界面也行 | opencode run "问题",跑完就退 | 07 |
场景一:理解陌生代码库
别一上来就"给我讲讲这个项目"——那样你得到的是一篇概括。更有效的做法是先要一张地图,再按块追问。
给我这个项目的整体架构做一个总结
重点关注 @src/api/ 目录下的路由设计
如果 @src/api/ 这种目录级引用让它一次读太多文件,就换成"先列文件、再逐个问":
先列出 @src/api/ 下面所有的路由文件,不要读内容。
然后告诉我哪个文件负责处理用户登录。
场景二:加一个新功能
先用 Plan 出方案,确认后再切 Build。这是整套用法里最省返工的一步。
# 1. 按 Tab 切到 Plan 模式,然后说需求
我想给用户系统加一个"最近登录时间"字段。
先分析一下需要改哪些文件,给我一个方案。
# 2. 看方案、提意见(这一步不要跳)
数据库迁移你打算怎么做?线上已经有数据了,不能停机。
# 3. 按 Tab 切回 Build,再让它动手
方案看起来不错,开始实现吧。
场景三:修一个具体的 bug
这个场景的关键是:把"能复现的现象"说出来,而不是只说"有 bug"。前者能让它验证自己的修复,后者只能让它猜。
@src/utils/auth.ts 里的 login 函数有问题,
用户输入空密码时不会报错。帮我修复并加上测试。
# 更好的版本:把复现步骤也给出来
@src/utils/auth.ts 的 login 函数在 password 传空字符串时返回成功。
我在 @src/utils/auth.test.ts 里加了这条用例但它失败了:
!npm test -- auth.test.ts
先看失败信息,再修 login,不要改测试。
场景四:代码重构
重构最容易失控,因为它涉及的文件多。先小范围试跑是这里唯一值得记住的技巧。
帮我把 @src/components/ 里的 class 组件
全部重构成 functional component + hooks
# 更稳的做法:先让它在一个文件上证明它理解了模式
先只改 @src/components/UserCard.tsx 这一个文件,
按 functional component + hooks 重写一遍,其他文件先别动。
改完告诉我你做了哪几处等价变换。
场景五:写测试
给 @src/services/api.ts 写单元测试,
覆盖正常流程和错误场景
想让它写得像你团队的样子,就把现有的测试文件也引用上——一个例子顶十句描述:
给 @src/services/api.ts 写单元测试。
格式和断言风格参考 @src/services/user.test.ts,
要覆盖正常流程、网络错误、以及返回空数据三种情况。
场景六:审查最近的改动
这一类最值得用 ! 前缀:你自己先把现场贴进去,它就不用猜"最近"是哪几次。
审查一下最近的改动,关注安全性和性能问题
!git diff HEAD~3
如果改动很多、你又只想看有风险的部分:
!git diff HEAD~3 --stat
上面这些文件里,只挑涉及认证和数据库查询的改动来审。
每条问题按"文件:行 · 问题 · 为什么危险 · 怎么改"列出来。
回头看一眼会发现它们都是同一个模板:目标(要什么) + 上下文(@文件 / !命令) + 约束(别改测试、只改一个文件、只看某几类) + 产出格式(列出来 / 加测试 / 给方案)。这四样缺哪个,它就只好自己猜哪个——而它猜错的时候,你花的返工时间比多打那两行字贵得多。
16十个最常见的坑
下面这些是真实会撞上的。前三类是"刚装完"的坑,中间是"用起来"的坑,最后两条是"你以为配好了其实没有"的坑——那两条不发脾气、不报错,最危险。
第一类:装完就卡住
| 症状 | 真实原因 | 怎么办 |
|---|---|---|
敲 opencode 提示命令不存在 | 安装目录不在 PATH 里;或者命令存在但 shell 缓存了旧的路径表 | Homebrew 装的一般在 /opt/homebrew/bin(Apple 芯片)或 /usr/local/bin;npm 装的是 npm bin -g 那个目录。加进 PATH 后重开终端 |
公司机器不允许 curl | bash | 那是一条"下载脚本直接执行"的命令,很多安全策略会拦 | 换包管理器那条(npm install -g opencode-ai 最常见),或者去 Releases 页面下对应平台的二进制 |
| Windows 上各种怪问题 | 原生 Windows 的终端与文件系统集成和 Linux 差异大 | 官方推荐用 WSL;opencode web 的文档也明确说 Windows 上请从 WSL 里跑 |
第二类:接不上模型
| 症状 | 真实原因 | 怎么办 |
|---|---|---|
| 界面里看不到任何模型 | 还没接提供商,或者接的那家被 disabled_providers 关掉了 | 敲 /connect;已经接过就用 opencode auth list 确认;再确认配置里没把它列进禁用清单 |
| 一直报认证失败 | Key 前后带了空格;或者 {env:…} 引用的变量根本没设(没设不会报错,只会变成空串) | 重新粘一次 Key;用 echo $ANTHROPIC_API_KEY 这类命令确认变量真的有值 |
| 提示模型名不存在 | 拼错了 provider/model 里的任一半 | 用 opencode models 把准确的名字打出来,照抄;刚上新模型时加 --refresh |
| 公司网络下连不上 | 要走代理或自建网关 | 在 provider.<名字>.options.baseURL 里换成你的地址 |
第三类:用起来不对劲
| 症状 | 真实原因 | 怎么办 |
|---|---|---|
| 它不懂我项目的习惯,答得笼统 | 没跑 /init,项目里没有 AGENTS.md | 跑一次 /init 并把它提交进 Git;规则分散时用 instructions 汇总(见 14) |
| 让大模型干杂活,账单涨得快 | 主模型和"起标题"这类小事共用了一个贵模型 | 配 small_model 指一个便宜模型;机械劳动时用 F2 临时切到小模型 |
| 对话越长它越笨、越慢 | 上下文快满了。它每次都要把整段历史重新读一遍 | 先用 /compact 压一次;话题已经换了就直接 /new。任务告一段落时 /new 比 /compact 更划算——后者本身就是一次大请求 |
| 说了半天它还是找错文件 | 没给上下文,它在靠猜 | 用 @ 把该看的文件点出来;要它先看现场就用 !命令(见 17) |
| 它说"找不到某个文件" | grep / glob 底层是 ripgrep,默认遵守 .gitignore,被忽略的文件它也看不到 | 在项目根建 .ignore,用 !node_modules/ 这种写法放开要搜的路径 |
最后两条:没有报错,但已经在出血
默认权限是全放行的。这意味着从你敲完第一句话起,它就可能已经在动你的源码,而且不会有任何提示。三个可靠的症状:你不确定它刚才改了什么、git status 里出现了你没写过的改动、或者它在 Build 模式下"顺手"改了别的东西。
修法:进生产项目前,在项目根目录的 opencode.json 里写 {"permission": {"bash": "ask", "edit": "ask"}}。之后养成一个习惯——每次让它改完,先 !git diff 看一眼再继续。
官方规则是"最后一条匹配的赢"。所以下面这两种写法的效果正好相反:
# ❌ 反了:git push 会命中后面那条 "*",结果所有命令都是 ask
"bash": { "git push *": "deny", "*": "ask" }
# ✅ 对了:把兜底的 "*" 放前面,具体规则放后面
"bash": { "*": "ask", "git status *": "allow", "git push *": "deny" }
另一个同源的坑:写 "git status" 挡不住带参数的命令,因为匹配的是解析后的完整命令(比如 git status --porcelain)。要挡住带参数的写法,得写 "git status *"。
排错:先跑这三条命令
出问题时不要凭感觉猜,先把机器上的事实读出来。这三条覆盖了绝大多数情况:
# 1. 版本对不对(也是确认 PATH 通不通)
opencode --version
# 2. 到底接了哪几家提供商
opencode auth list
# 3. 当前这份配置最终解析成了什么(能看出哪一层盖掉了哪一层)
opencode debug config
# 附:想看它把请求发去哪、报了什么,就把日志级别压到 DEBUG
opencode --print-logs --log-level DEBUG
# 附:怀疑是插件在捣乱时,用"纯净模式"跑一次(不加载外部插件)
opencode --pure
配置是八层合并的,所以"我明明写了对的"和"它就是没生效"可以同时为真。opencode debug config 会把合并之后的结果打印出来——托管配置的键也会出现在里面(官方文档里就举了这个例子)。所以怀疑配置没生效时,别看源文件,看这个输出。
17成对示例:同一件事的两种问法
同一件事,两种说法,结果的差距通常不是"它聪明不聪明",而是你有没有把它需要的东西放到它面前。下面四对都是可以直接对照的。
第一对:修一个 bug
帮我修复这个 bug
它会怎么做:先问你是哪个 bug,或者自己去猜一个。你花在来回澄清上的时间,通常比多打两行字更贵。
@src/auth.ts 的 login 函数在空密码时不报错。
!npm test -- auth.test.ts
先看失败信息,再改 login,不要改测试。
它会怎么做:直接读文件、看测试输出、改实现,而且知道"不能靠改测试让它变绿"。
第二对:让它改代码但别乱改
把这个项目里的 class 组件都改成函数组件
问题在哪:范围是整个项目,它可能改几十个文件、动到测试和样式,而你要的是"先看一下它理解得对不对"。而且这种规模你没法一口气审完 diff。
先只改 @src/components/UserCard.tsx 这一个文件,
按函数组件 + hooks 重写,其他文件先别动。
改完告诉我你做了哪几处等价变换。
好在哪:一个文件的 diff 你能三分钟看完,而且"告诉我做了哪几处变换"这句会迫使它把隐含判断说出来——判断错了你当场就能发现。
第三对:问它"为什么"
我的构建为什么这么慢?
它会怎么做:只能给一堆通用的可能性清单。它看不到你的构建到底慢在哪一步。
!npm run build --profile
上面是我们项目的构建输出,最慢的三步是哪些?
只分析这三个,不要重写构建配置。
好在哪:它回答的是你项目的事实,不是通用建议。"不要重写构建配置"这句同时挡住了它的过度热情。
第四对:权限
# 什么也不配 = 所有操作都不问就执行
# 它可能直接跑 git push、跑迁移脚本
风险在哪:不是"它会故意搞破坏",而是它理解错了需求、或者顺手多做了几步——而这几步在默认权限下不会有任何拦截。
{
"permission": {
"*": "ask",
"read": "allow",
"bash": {
"*": "ask",
"git push *": "deny"
}
}
}
好在哪:读还是放开的,所以它照样能理解项目;但"改"和"跑命令"都要你点头,而"推代码"根本轮不到它做。
把好写法归纳成一张模板
四对看下来,会发现"好"的那一栏总是同四样东西。它不是提示词工程,就是把任务说清楚的常识:
| 要素 | 它回答什么 | 怎么写 |
|---|---|---|
| 目标 | 我要什么结果 | 一句话说清是"修好""加上""解释"还是"只出方案" |
| 上下文 | 它该看哪些材料 | @文件 指出该看的代码;!命令 把当前现场贴进去 |
| 约束 | 哪些事不许做 | "不要改测试""只改这一个文件""不要重写构建配置" |
| 产出格式 | 结果长什么样 | "列出来""按文件:行 · 问题 · 为什么危险 · 怎么改" |
这套模板能大幅降低"它跑偏"的概率,但它不能保证结果正确。模型会答错、会在长对话里忘记前面的约束、会自信地编出一个不存在的函数名——所以"改完 !git diff 看一眼"和"关键改动自己跑一遍测试"这两件事没有替代品。把 OpenCode 当成一个产出很快、但需要验收的合作者,比当成一个可信赖的自动机器更接近事实。
18什么时候别用它
这一节不属于开头(开头该给的是"怎么用"),但它确实该存在——因为它决定你会不会在错误的地方浪费一周。
五件它不适合的事
| 场景 | 为什么不适合 | 更合适的做法 |
|---|---|---|
| 代码不能离开你的机器,而你又不想跑本地模型 | 模型调用是把上下文发到提供商的服务器上;联网搜索类工具还有额外的外部依赖 | 接本机的 Ollama / LM Studio;或者用 permission 把 webfetch、websearch 直接 "deny" |
| 你只想"敲字时有个补全" | 它的交互模型是"你下达任务、它整段动手",不是逐字提示;为这个用途装它,反而多一个终端窗口要管 | 继续用编辑器里的补全工具,两者可以并存 |
| 追求"完全无人值守地改对代码" | 它产出很快,但没有验收能力:它不知道你的业务规则,也不承担后果 | 把它当"需要验收的合作者":改完看 diff、跑测试;关键路径上留人工过目 |
| 在一个没有 Git 的目录里指望能回滚 | /undo 和 /redo 的内部实现依赖 Git,项目不是仓库时这两个命令不工作 | 先 git init 并做一次初始提交;或者严格用 Plan 模式,只让它出方案 |
| 临时跑一次、又不想装东西 | 它是个本地程序,装了才有;虽然有 Docker 那条路,但那也要能拉镜像 | 用 docker run -it --rm ghcr.io/anomalyco/opencode,或者干脆用你已有的工具 |
它和"直接问模型"的分工
这一条能帮你省订阅费,也能帮你省时间:
要读你项目里好几个真实文件才能回答的问题;要真的改文件、跑命令、看输出的活;批量的机械改动;需要反复"改—跑—看结果"的循环。
问一个和你的代码库无关的技术概念;让它解释一段你随手贴过去的小代码;写一段通用正则或 SQL;讨论方案但不需要它读仓库。这些不需要"手和脚",用带项目上下文的代理反而更绕。
① 别省 diff 的眼睛——它是产出工具,不是审查工具;② 别省权限配置——默认全开是为了让你上手快,不是让你长期这么用;③ 别省密钥的隔离——用 {env:…} 或 {file:…},别把 Key 直接写进要提交进 Git 的 opencode.json。顺带一提,.env 系列文件默认就在 read 权限的拒绝清单里,这是官方给你的默认保护,别把它打开。
19术语对照表
下面这十七个词在文中反复出现。第三列不是翻译,是"它在整套体系里为什么重要、以及别拿它当什么用"——同一个词用错地方,往往就是一次配置事故。
| 术语 | 用日常语言说是 | 为什么重要 / 何时用 · 别当什么用 |
|---|---|---|
| 编程代理(coding agent) | 能自己读代码、改代码、跑命令的 AI 程序,不只是给建议 | OpenCode 的核心定位。它的产出是"改好的文件",所以你把话当"下任务"说,而不是当"提问"说。别当补全插件用——它的价值恰在"会自己动手",你只想补全就不需要它 |
| 斜杠命令(slash command) | 在输入框里敲 / 开头触发的命令,如 /init | 这些是"你操作工具"的开关,不是发给模型的话——所以 /undo 会撤销而不是让模型"撤销一下"。别在给模型的提示词里混用斜杠命令,正文里它不生效 |
| TUI | 终端用户界面;就是敲 opencode 之后那个全屏窗口 | 日常主界面,四块区域(顶部状态 / 中部消息 / 底部输入 / 右下角模式)。别把它当成"一堆命令行参数"——它是有输入框、有快捷键、有面板的完整应用 |
| 提供商(provider) | 卖"模型调用能力"的那家公司或服务,比如 Anthropic、OpenCode Zen、你本机的 Ollama | 换提供商意味着换账单、换 Key、可能换模型清单。别和"模型"混为一谈——同一件货在不同店家那里价格和表现都可能不同 |
| 模型(model) | 真正做"思考"的那个大语言模型 | 配置里写成 provider/model-id,斜杠前是店家、后面是货;换它就是换脑力和价钱。别以为"选了模型 = 装了模型"——它始终跑在提供商那边 |
| 主代理(primary agent) | 你正在对话的那个助手,内置有 Build 和 Plan | 用 Tab 轮换;它决定"这一轮用什么模型、带什么权限"。别在模糊需求下直接用 Build 让它乱改——先切 Plan 出方案,是省返工的关键分流 |
| 子代理(subagent) | 被主代理派去做专项活的小助手,内置有 General、Explore、Scout | 把"读一大堆文件"的脏活放到它自己的会话里干,只把结论带回来,从而让主对话的上下文保持轻量。别把需要主决策的事交给它——它看不到你们刚才的讨论 |
| 工具(tool) | 代理手上真正能操作项目的动作:读、改、写、搜、跑命令、抓网页…… | 权限就是按工具配的(bash、edit、read……)。别把工具和权限混着说:工具是"它能做什么",权限是"允不允许它做" |
| 权限(permission) | 给每个工具定的三档态度:放行 / 问你 / 禁止 | OpenCode 默认几乎全放行,所以这是唯一一道由你控制的闸。别当它是可选装饰——不配它,AI 就会在你没看的时候改文件、跑命令 |
| AGENTS.md | 项目根目录里写给 AI 的"说明书"(类似 Claude Code 的 CLAUDE.md) | 用 /init 生成;它决定"AI 知不知道你项目的命令和约定"。别把历史备注和临时状态灌进去——这份文件每次会话都会被读一遍,写多了等于每次都在花你的上下文预算 |
| MCP | Model Context Protocol,给模型"接外部工具"的一套标准接口 | 要接数据库、Jira、GitHub 这类外部能力时用;接上之后那些工具和内置工具一样出现在模型手里。别一次挂一堆——每个 MCP 服务器都往上下文里塞工具,官方点名 GitHub 那个特别占 token |
| LSP | 语言服务协议;让编辑器/工具能问"这个符号定义在哪、谁引用了它" | 让 AI"跳定义"而不是"翻全文找",在类型化语言的大项目里能明显减少读文件量。别以为它是必装项——OpenCode 里它是实验性工具,要显式开 OPENCODE_EXPERIMENTAL_LSP_TOOL=true |
| 上下文窗口(context window) | 模型一次最多能读进去的文本量,有上限 | 它解释了"为什么对话越长它越笨"——历史占满了,新的信息就挤不进去。别把"它忘了"理解成它变傻了:那是窗口满了,用 /compact 或 /new |
| Token | 模型读写文本的最小计量单位;大致可以理解成"一小段字",中文里常常一个字或两个字算一个 | 它是计费和容量的共同单位:你付的钱按 token 算,模型的上下文上限也按 token 算。别拿"字数"估成本——代码和中文的 token 密度差很多 |
| 压缩(compaction) | 把一长段对话压成一份摘要,继续往下聊 | 长会话续命时才值得花——它本身也是一次不小的请求。别把压缩当日常操作:话题已经换了,/new 开一段新的通常更省更干净 |
| 快照(snapshot) | 记录文件改动的一套内部机制,是 /undo 与 /redo 的底座 | "大胆让它改、错了能退"的前提。别以为它在任何目录都能用:它依赖 Git,项目不是仓库时这两个命令不工作。大仓库里它还可能有索引慢、占磁盘的问题,能关(关了就不能界面回滚) |
| 前缀键(leader key) | 快捷键的"开场白":先按它,再按一个字母,两个键合起来才算一条 | OpenCode 默认是 Ctrl+X,文档里写成 <leader>;默认等 2000 毫秒。别单独按它就等结果——它自己什么都不做;不习惯就在 tui.json 的 keybinds 里换掉 |
| 确认弹窗(ask) | 权限设成"问你"之后弹出来的那个框,有三个选项:只批这一次 / 这一类都批 / 拒绝 | 它是"你参与决策"的唯一入口,所以看清它写的命令再点。别顺手点"这一类都批"——那个批准只对当前这段会话有效,但覆盖的范围是"同一类模式"(比如所有 git status*),点错一次等于这段会话里这类命令全放行 |
提供商 vs 模型:店家 vs 货。主代理 vs 子代理:跟你说话的 vs 被派出去的。AGENTS.md vs Plan 模式:前者让 AI"知道怎么做",后者让 AI"不许动手"——一个管脑子,一个管手。
20一页纸速查
这一节是留给你第二遍回来查的。不需要从头读。
装完到用顺:一份检查清单
| # | 做这件事 | 怎么知道做对了 |
|---|---|---|
| 1 | 装上并确认命令可达 | opencode --version 吐出版本号(本文校对时是 1.18.30) |
| 2 | 接上一个提供商 | opencode auth list 能列出你接的那家 |
| 3 | 在一个真实项目里初始化 | 项目根目录多了一个 AGENTS.md,内容讲的是"命令 + 结构 + 约定" |
| 4 | 跑通一次只读问答 | 你用 @文件 提问,它答的内容确实是那个文件里的东西 |
| 5 | 走一遍 Plan → Build | Plan 里它先给你方案;切到 Build 说"开始"它才动手 |
| 6 | 验证 /undo 真的能撤 | 它改完之后 /undo,!git diff 回到改动前的状态 |
| 7 | 把权限收紧 | opencode debug config 的输出里能看到你配的 "bash": "ask" |
| 8 | 把 AGENTS.md 交进 Git | !git status 里它不在未跟踪列表里 |
按"我想做什么"查
第三列写清"在哪儿敲",因为其中有几条不是敲在 OpenCode 的输入框里的。
| 我想做什么 | 敲什么 | 在哪儿敲 |
|---|---|---|
| 装上 | curl -fsSL https://opencode.ai/install | bash | 系统终端 |
| 确认版本 | opencode --version | 系统终端 |
| 升级 / 卸载 | opencode upgrade / opencode uninstall | 系统终端 |
| 接提供商 | /connect(或 opencode auth login) | TUI 里 / 系统终端 |
| 看接了哪几家 | opencode auth list | 系统终端 |
| 看有哪些模型 | opencode models(加 --refresh 刷新) | 系统终端 |
| 进项目 | cd 项目目录 然后 opencode | 系统终端 |
| 让 AI 认识项目 | /init | TUI 输入框 |
| 引用文件 | @路径/到/文件.ts | 消息里 |
| 先跑个命令看现场 | !git status | 消息里 |
| 切开发 / 分析模式 | Tab / Shift+Tab | TUI 键盘 |
| 换模型 | /models,或 F2 在最近用过的之间切 | TUI |
| 撤销 / 重做 | /undo / /redo(或 ctrl+x u / ctrl+x r) | TUI |
| 压上下文 / 开新会话 | /compact / /new | TUI |
| 切会话 | /sessions(别名 /resume、/continue) | TUI |
| 分享 / 取消分享 | /share / /unshare | TUI |
| 导出成 Markdown | /export | TUI |
| 看它跑了什么 | /details | TUI |
| 退出 | /exit(别名 /quit、/q) | TUI |
| 不用界面问一句 | opencode run "你的问题" | 系统终端 |
| 浏览器里用 | opencode web --port 4096 | 系统终端 |
| 自己下了个会话文件想导入 | opencode import session.json | 系统终端 |
| 查配置到底解析成了什么 | opencode debug config | 系统终端 |
| 管 MCP 服务器 | opencode mcp add / opencode mcp list | 系统终端 |
四份配置模板,按需抄
下面四段覆盖了绝大多数人的实际需求。第一段是最该抄的那一份。
# ① 最小安全配置:读放开、改和跑命令要问、推代码直接禁
{
"$schema": "https://opencode.ai/config.json",
"permission": {
"*": "ask",
"read": "allow",
"grep": "allow",
"glob": "allow",
"bash": {
"*": "ask",
"git status *": "allow",
"git push *": "deny"
}
}
}
# ② 主模型 + 杂活模型分开,密钥走环境变量
{
"$schema": "https://opencode.ai/config.json",
"model": "anthropic/claude-sonnet-4-5",
"small_model": "anthropic/claude-haiku-4-5",
"provider": {
"anthropic": {
"options": { "apiKey": "{env:ANTHROPIC_API_KEY}" }
}
}
}
# ③ 一个只读的子代理(审查用,永远不改文件)
# 存成 ~/.config/opencode/agents/review.md,或用 /review 点名它
---
description: Reviews code for quality and best practices
mode: subagent
permission:
edit: deny
bash: ask
---
You are in code review mode. Focus on code quality, edge cases,
performance, and security. Provide feedback without direct changes.
# ④ 一条自定义命令:把"跑测试 + 只看失败 + 给建议"压成一个词
# 存成 .opencode/commands/test.md,然后敲 /test
---
description: Run tests with coverage
agent: build
---
Here are the current test results:
!`npm test`
Focus on the failing tests and suggest fixes.
99一句话总结
OpenCode 是一个跑在终端里的开源编程代理——它本身不含智能,靠你 /connect 接上的提供商提供模型;你用 @ 把文件喂给它、用 ! 把现场贴给它、用 Tab 在 Plan 和 Build 之间切换来先出方案再动手、用 /undo 给自己留一条退路,而这一切的前提是先把默认全开的权限关到"改东西要问你"。
98参考来源与证据边界
来源清单
本文的每条事实都对着下面这些一手来源核过。日期栏写的是抓取时页面上标注的"最后更新"或接口返回的时间。
| 来源 | 用它核了什么 | 抓取日 |
|---|---|---|
| github.com/anomalyco/opencode | 仓库描述原话、许可证、Star 数、贡献者页数、默认分支与提交数 | 2026-09-12 |
| GitHub Releases API(最新版) | 版本号 v1.18.30、发布日期 2026-09-09、该版更新内容 | 2026-09-12 |
| npm registry:opencode-ai | npm 上的最新版本号(与 GitHub release 一致) | 2026-09-12 |
| opencode.ai/docs(Intro) | 安装方式全清单、Prerequisites、/connect 三步、Plan/Build 与 /undo 的官方叙述 | 2026-09-10 页脚标注 |
| docs/tui | 全部斜杠命令与各自别名、/undo 依赖 Git 的原话、tui.json 全部选项与默认值、EDITOR 设置 | 2026-09-10 页脚标注 |
| docs/cli | CLI 子命令与 flag、upgrade/uninstall 的 flag、环境变量全表(含 Experimental 一节) | 2026-09-10 页脚标注 |
| docs/keybinds | 默认键位:<leader>u/<leader>r、f2、tab、子会话的 ↓/→/←/↑ 等具体映射 | 2026-09-10 页脚标注 |
| docs/config | 八层优先级的确切顺序、合并语义、常用配置项与默认值、{env:}/{file:} 替换、托管配置路径 | 2026-09-10 页脚标注 |
| docs/permissions | 三档动作、十四个权限键、通配符与「最后匹配者胜」、--auto 的边界、doom_loop 与 external_directory 的默认值 | 2026-09-10 页脚标注 |
| docs/agents | 主代理/子代理的类型定义、五个内置代理的定位、三个隐藏系统代理、全部 Agent 选项与 steps/maxSteps 的废弃说明 | 2026-09-10 页脚标注 |
| docs/tools | 全部内置工具清单与各自权限键、ripgrep 与 .ignore 的行为、websearch 的前置条件、LSP 工具的开关 | 2026-09-10 页脚标注 |
| docs/mcp-servers | local/remote 两种类型与全部字段、超时默认 5000ms、OAuth 自动处理、上下文占用的官方提醒 | 2026-09-10 页脚标注 |
| docs/rules | AGENTS.md 的官方示例、四处查找位置与优先级、/init 的行为、Claude Code 兼容与三个开关变量 | 2026-09-10 页脚标注 |
| docs/references | 引用别名的字段表、别名命名限制、Git 引用异步刷新、引用与外部目录权限的关系 | 2026-09-10 页脚标注 |
| docs/commands | 自定义命令的两种写法、$ARGUMENTS/$1、!`命令`、@文件 三种占位语法 | 2026-09-10 页脚标注 |
| docs/providers | 「通过 AI SDK 与 Models.dev 支持 75+ 提供商」原话、凭证文件位置、baseURL/blacklist/whitelist 的用法 | 2026-09-10 页脚标注 |
| docs/zen | 「只有少数模型适合当编程代理」的原话、Zen 的三步建立理由、模型与端点清单 | 2026-09-10 页脚标注 |
| docs/web · docs/share · docs/ide | web 的 hostname/密码注意点、分享链接的公开性原话与三种模式、IDE 扩展的自动安装与排错步骤 | 2026-09-10 页脚标注 |
证据边界:哪些地方我核不到一手来源
- 「16M+ 月活跃开发者」这类使用量数字没有来源。它在网上流传,但官方文档和仓库都不提供这个口径。本文因此没有把它当作事实写进正文,hero 里的四个数字全部改成了能从 GitHub / npm 接口直接复核的量。
- 贡献者数量在不同口径下差一倍以上。GitHub 贡献者接口按每页 100 条返回时,最后一页是第 5 页,也就是约 401–500 人;而按每页 1 条返回时最后一页是第 456 页,也就是 456 人。两个数字都对,区别是接口把匿名与合并后提交者的计法。本文取的是"每页 100 条、共 5 页"这个口径,写成 401,并在 hero 里标注了这是 GitHub 计数。旧版本写的「950+ 贡献者」在两种口径下都对不上,见下面的修订清单。
- 「13K+ 代码提交」也已过期。按默认分支
dev的提交接口读,最后一页是第 15717 页(每页 1 条),所以是约 15,717 次提交。 - 斜杠命令清单取自文档,而不是运行中的程序。我核对的是
docs/tui页面上的命令列表,没有在本机装一份去逐条敲。如果你装的版本里/themes之类不存在了,以你的实际界面为准——这类清单在上游是会更新的。 - 截图与界面布局描述来自文档文字,不是截图。文档没有配图,所以"顶部/中部/底部/右下角"这四块是依据文档对界面元素的叙述改写的;不同版本的外观细节可能不同。
- MCP 的
type取值变化只找到文档层面的证据。旧版本里写"type": "stdio",现在文档只列local与remote,且旧的tools配置被明确标为废弃。我没有找到"stdio从哪个版本开始不再接受"的变更记录,所以正文只说"旧教程里的写法和现在的取值不一样",没有断言它到底还能不能用。
时效说明
本文的档案是 tool、时效标记是 evolving(演进型),校对日期 2026-09-13,版本锚点是 anomalyco/opencode v1.18.30(2026-09-09 发布)。这个项目迭代很快——光是在我核对的那一天,默认分支就有新提交。所以下面这几类内容最可能先过期,请以官方源为准:
- 斜杠命令清单与键位:命令会增删,别名会变(
/compact的别名/summarize、/sessions的别名/resume、/continue就是这类变化的结果)。 - 内置代理与内置工具清单:官方文档里已经有多个工具带"实验性"标注(
lsp、websearch的开关方式),这类功能最容易变。 - 配置项的默认值:比如权限默认值、
subagent_depth的默认层数、MCP 超时的 5000 毫秒。默认值一变,文中"默认是……"那几句就过期。 - 版本号与仓库数字:hero 里的四个数字、以及正文里的
1.18.30,都是 2026-09-12/13 的快照。
三条命令就能把本文最核心的事实自己验一遍(这是全文唯一需要你自己动手的判据):
# 1. 版本锚点是不是真的
curl -sSL https://registry.npmjs.org/opencode-ai/latest | python3 -c "import json,sys;print(json.load(sys.stdin)['version'])"
# 2. hero 里的仓库数字
curl -sSL https://api.github.com/repositories/975734319 | python3 -c "import json,sys;d=json.load(sys.stdin);print(d['full_name'],d['stargazers_count'],d['license']['spdx_id'])"
# 3. 斜杠命令清单的原页面
curl -sSL https://opencode.ai/docs/tui/ | grep -o '/themes' | head -1
第 3 条用的是最糙的抓法(直接在 HTML 里搜字符串),够用来判断"这个命令还在不在文档里"。想更准就打开那个页面自己看命令列表。