Agent、命令与 Skills
七个内置 agent 的权限设计、Markdown 定义与覆盖规则、命令模板的展开顺序,以及 SKILL.md 的发现与暴露方式。
三者走同一条加载路径——都能写成配置目录下的 Markdown 文件(见配置系统),frontmatter 放元数据,正文放提示词。区别在于触发者:agent 是模型的身份,命令是用户敲的快捷方式,skill 是模型按需翻开的说明书。
Agent 的形状
packages/opencode/src/agent/agent.ts 里的 Agent.Info 字段不多:name、description、mode、hidden、model 与 variant、temperature 与 topP、prompt、steps、color、options,以及最关键的 permission 规则集。
mode 三选一:primary 是可在界面里切换的主 agent,subagent 只能被 task 工具派生,all 两者皆可。prompt 一旦设置就会替换按模型挑选的默认系统提示词,而不是追加(session/llm/request.ts:60)。
七个内置 agent
| agent | mode | 特点 |
|---|---|---|
build | primary | 默认 agent,额外放开 question 与 plan_enter |
plan | primary | 编辑一律拒绝,只放行 .opencode/plans/*.md 与数据目录下的计划文件;不能派生 general |
general | subagent | 通用研究与多步任务,禁用 todowrite |
explore | subagent | 先拒绝一切,再只放行 grep、glob、list、bash、webfetch、websearch、read |
compaction | primary(隐藏) | 上下文压缩专用,拒绝所有工具 |
title | primary(隐藏) | 生成会话标题,temperature 为 0.5 |
summary | primary(隐藏) | 生成摘要 |
隐藏的三个与 explore 自带 agent/prompt/*.txt 提示词;plan 的行为约束则由 session/reminders.ts 以合成文本附在用户消息后。所有 agent 共享一套默认权限:
const defaults = Permission.fromConfig({
"*": "allow",
doom_loop: "ask",
external_directory: {
"*": "ask",
...Object.fromEntries(whitelistedDirs.map((dir) => [dir, "allow"])),
},
question: "deny",
plan_enter: "deny",
plan_exit: "deny",
// mirrors github.com/github/gitignore Node.gitignore pattern for .env files
read: {
"*": "allow",
"*.env": "ask",
"*.env.*": "ask",
"*.env.example": "allow",
},
})(packages/opencode/src/agent/agent.ts:119-136)
基调是「默认放行,但对三件事留心」:疑似死循环(doom_loop)、访问工作区外的目录、读取 .env。白名单目录包括工具输出截断目录、临时目录、skill 目录与 references 目录。规则怎么求值见权限系统。
覆盖与新增
用户配置按名字并入这张表(agent.ts:267-294):disable: true 直接删除;已有 agent 逐字段覆盖,options 深合并,permission 规则追加到末尾——求值用的是 findLast,追加即覆盖。新名字从 mode: "all" 和默认权限起步;frontmatter 里不认识的键收进 options,旧的 tools 与 maxSteps 在解码时转成 permission 与 steps。
放在 .opencode/agents/reviewer.md 的只读审查 agent:
---
description: 审查最近的改动,只提意见不改代码
mode: subagent
model: anthropic/claude-sonnet-4-5
temperature: 0.1
steps: 20
permission:
edit: deny
bash:
"*": ask
"git diff*": allow
---
你是代码审查员。先用 git diff 查看改动,再逐个文件列出问题与建议。default_agent 可以换掉默认的 build,但目标必须存在,且不能是 subagent 或隐藏 agent,否则直接抛错。
子代理:谁能看见,能走多深
task 工具的描述只列出 mode 不为 primary 的 agent,并按调用方的 task 权限过滤(tool/registry.ts:265-278)。嵌套深度由 subagent_depth 控制,默认 1,即子代理不能再派生子代理。子会话权限由 agent/subagent-permissions.ts 推导:
Parent agent restrictions only govern that agent; the subagent's own permissions determine its capabilities.
具体只继承父会话的 deny 与 external_directory 规则,子代理自己没授权时再追加 todowrite、task 的拒绝。详见子代理、待办与提问。
opencode agent:让模型写 agent
opencode agent create 以 agent/generate.txt 为系统提示词,让模型按一句描述生成 identifier、whenToUse、systemPrompt(并告知已占用的名字),写成 agents/<identifier>.md。权限按「权限键」而非工具名勾选——注释的解释是多个工具可能共用一个权限,例如 write、edit、apply_patch 都受 edit 管——没勾的写成 deny。参数齐全时可以非交互运行;opencode agent list 列出所有 agent 及其展开后的权限。
命令的四个来源
packages/opencode/src/command/index.ts 依次汇总:
- 内置
/init(引导生成AGENTS.md)与/review(以子任务审查未提交改动、提交、分支或 PR); - 配置里的
command与commands/**/*.md,同名时覆盖内置; - MCP 服务器的 prompt,名为
server:prompt,参数依次映射为$1、$2……; - skill,仅在不与已有命令重名时加入。
模板的展开顺序
执行在 SessionPrompt.command()(session/prompt.ts:1356):
- 位置参数:参数按空白切分(引号内算一个,
[Image N]也算一个),替换$1、$2……,编号最大的占位符吞下剩余全部参数; - 整串参数:
$ARGUMENTS替换为原始参数;两种占位符都没有时,参数追加到模板末尾; - Shell 注入:
!`cmd`用配置的 shell 执行,输出替换进模板; - 引用:
@path指向存在的文件或目录时附为文件部件,否则若有同名 agent,就变成 agent 部件。
最后决定在哪里跑:
const isSubtask = (agent.mode === "subagent" && cmd.subtask !== false) || cmd.subtask === true(session/prompt.ts:1439)目标是 subagent 时默认作为子任务运行,subtask 可以显式开关。模型取值顺序为:命令的 model → 命令所指 agent 的模型 → 调用时传入的模型 → 会话当前模型。插件可以在 command.execute.before 钩子里改写即将发出的部件。
---
description: 跑指定测试文件并修复失败
agent: build
---
运行测试文件 $1 并修复所有失败。补充说明:$2
最近的提交:
!`git log --oneline -5`
遵循 @AGENTS.md 里的测试约定。Skills:发现、暴露、加载
packages/opencode/src/skill/index.ts 按如下顺序扫描 SKILL.md:
| 位置 | 模式 |
|---|---|
~/.claude/、~/.agents/,以及项目内逐级向上的同名目录 | skills/**/SKILL.md |
| 所有配置目录 | {skill,skills}/**/SKILL.md |
配置 skills.paths | **/SKILL.md |
配置 skills.urls | 拉取 index.json 后下载到缓存 |
第一行受两个开关控制:OPENCODE_DISABLE_EXTERNAL_SKILLS 全部跳过;OPENCODE_DISABLE_CLAUDE_CODE_SKILLS(或更宽的 OPENCODE_DISABLE_CLAUDE_CODE)只跳过 .claude。frontmatter 必须有字符串 name,没有 description 的 skill 不进提示词清单。远程 skill 按 index.json 里的 version 更新:先下载到暂存目录,确认 SKILL.md 存在后再用 rename 换入。
还有一个内置 skill customize-opencode,注释道出了来由:
The model's intuition for what an opencode.json should look like is often wrong, and opencode hard-fails on invalid config, so users hit cryptic startup errors. Loading this skill when the model is asked to touch opencode's own config files gives it the actual schemas instead of guesses.
它先于磁盘扫描注册,用户可以用同名 skill 覆盖它。
暴露分两层:系统提示词里放一份 <available_skills> 详单(按 agent 的 skill 权限过滤);正文等模型调用 skill 工具才加载,返回正文、所在目录和最多 10 个随附文件路径,方便继续读 scripts/ 之类的资源。
新内核 packages/core/src/skill.ts 把来源抽象为目录、URL、内嵌三类,目录顶层的 *.md 也算 skill,frontmatter 新增 slash 标记;加上 V2 设计「移除 command、由 skills 承担可复用工作流」的决定,命令与 skill 正在合流。
下一篇:MCP 集成——本地与远程服务器的连接、工具命名、OAuth 流程与 opencode mcp 命令。