Penpot 的 OpenCode 命令 resolve-git-conflicts:一个可复用的 AI Agent Git 冲突解决工作流设计
Penpot 仓库在 .opencode/commands/ 目录下维护了一套面向 OpenCode 智能体的命令定义,其中 resolve-git-conflicts.md 用五个阶段规定了 AI Agent 处理 Git 冲突的完整规程:只读分析、先出方案、批准后再改、暂存校验、汇报即止。读完后你不仅能理解这条命令的每一条规则为何存在,还能把"Agent 解决冲突、人类收尾 rebase"这套职责分离模式搬进自己的 AI 辅助开发流程。
命令文件的定位:OpenCode 命令体系中的一个专职环节
该文件是一个带 YAML frontmatter 的 Markdown 命令定义:
---
description: Resolve local git conflicts and stage the resolved files with git add — never continues the rebase
agent: build
---
description用一句话概括了命令的职责边界——解决冲突并用git add暂存结果,永远不推进 rebase;agent: build指明该命令由 OpenCode 的 build 型智能体执行,即拥有读写代码和运行命令能力的角色,而不是只读的分析型角色(对照 planner skill 中"analysis-only"的只读角色定位)。
description 里那句 "never continues the rebase" 与正文开头"you must never run git rebase --continue..."、Phase 5 末尾的再次强调形成了三处重复。这种刻意的冗余是 LLM 提示工程的常见手法:对影响面最大的安全红线在元数据、开头、结尾各钉一次,提高模型遵守的概率。
同目录下的两条命令恰好构成一个完整的工作流闭环,理解它们的分工有助于把握 resolve-git-conflicts 的边界:
| 命令 | 职责 | 是否触碰 Git 历史 |
|---|---|---|
| implement-plan.md | 建 issue、建 issue-NNNN 分支、实现计划、经 create-commit skill 提交 |
只做本地 commit,不 push |
| review.md | 对计划或代码做审查,输出结构化结论 | 完全不修改代码 |
| resolve-git-conflicts.md | 解决 rebase/merge 等产生的本地冲突并暂存 | 只编辑文件 + git add,不推进、不提交、不 push |
再结合仓库根目录 AGENTS.md 中的 HARD RULES——"Never git push, force-push, or modify git origin"、"Never amend a commit that has been pushed"——可以看出 Penpot 给智能体划定的边界是一致的:push、远程、历史改写这些不可逆/共享状态的操作一律留给人类,Agent 只在本地工作区内活动。resolve-git-conflicts 的"永不 git rebase --continue"正是这一边界在冲突场景下的具体化。
Phase 1 — 只读理解:在动任何文件之前弄清冲突全貌
原文 Phase 1 的全部要求(第 12–18 行):
- 运行
git status,检测当前处于哪种冲突状态(rebase、merge、cherry-pick 等),并列出所有冲突文件; - 对每一个 unmerged 文件,在不修改任何东西的前提下理解现状:
- 读取文件内容,定位冲突标记
<<<<<<<、=======、>>>>>>>; - 用
git show <ours>:<file>和git show <theirs>:<file>分别查看两侧在对象库中的原始版本,再用git log/git show查看相关提交,理解每一侧的意图; - 判断每一侧改了什么、为什么改、二者应当如何合并。
- 读取文件内容,定位冲突标记
这里有两个值得注意的技术点:
-
git show <ours>:<file>的用法:在 rebase 进行中时,:ours指 rebase 目标分支(被重放提交所基于的那一侧),:theirs指正在重放的提交——与 merge 语义恰好相反。要求 Agent 先查看两侧的完整对象版本而不是只看带标记的工作区文件,正是为了拿到双方改动的"干净基线",避免被冲突标记干扰判断。 -
"fragile state"与仓库工具链的呼应:Penpot 的 devenv 工具 manage.sh 中的
assert-clean-git-state函数检测的正是同一组"脆弱状态"——[ -d .git/rebase-apply ] && fragile="$fragile rebase-apply" [ -d .git/rebase-merge ] && fragile="$fragile rebase-merge" [ -f .git/MERGE_HEAD ] && fragile="$fragile merge" [ -f .git/CHERRY_PICK_HEAD ] && fragile="$fragile cherry-pick" [ -f .git/index.lock ] && fragile="$fragile index.lock"即 rebase 进行到一半(
rebase-apply/rebase-merge目录)、merge 到一半(MERGE_HEAD)、cherry-pick 到一半(CHERRY_PICK_HEAD)、以及 index 被锁。devenv 文档也明确写道:处于这些状态时,工作区同步(--sync)会被整体阻止,因为"把一次进行到一半的 rebase 复制进所有工作区会让每个实例都停在同一个坏状态"。也就是说,本命令要处理的冲突状态,恰好是仓库多工作区开发流程中唯一会"冻结"整条工具链的状态——尽快、正确地把它走完是有实际工程价值的,而 Phase 1 的"只读"约束保证 Agent 在诊断阶段绝不会把状态搞得更糟。
Phase 2 — 先给方案:每个文件都要说清"两边各干了什么、我准备怎么合"
原文 Phase 2(第 20–27 行)是整个命令中最"人类中心"的一段:
- 动任何文件之前必须先向用户呈现明确方案。对每个冲突文件说明三件事:
- 每一侧改了什么、为什么;
- 建议的解决方案及其推理依据;
- 两侧如何组合——原文给出的判据是:"both additive → merge(两边只是各自新增 → 直接都保留);both modify the same code → keep the semantically correct version, merging intent from both sides when clear from code and context(两边改了同一段代码 → 保留语义正确的那一版,当代码和上下文足够清楚时,把两侧的意图融合起来)";
- 只在真正无法判断时才提问。能从代码、commit message 或上下文推断出来的,一律不要问;只有那些"不可判定且会改变结果"的决定——例如相互冲突的产品决策、应当丢弃哪一侧——才值得提问;
- 所有提问集中到方案末尾的 "Open Questions" 小节,让用户带着完整上下文一次性作答,而不是被碎片化地打断;
- 在用户接受方案(并回答 open questions)之前,不编辑、不暂存、不做任何修改。
这条规则的设计意图值得展开:冲突解决是典型的"多解问题"——机械上能合并不代表合对了。把"方案评审"前置,等于给 Agent 的每个合并决定加了一道人类确认闸,且确认成本被集中压缩到一次交互中。这与同仓库 review 命令 的"Every finding must be real and actionable(不要编造问题)"、以及 create-commit skill 中"commit 前审查 git diff --staged、发现与声明意图不符就 STOP 并告知用户"的防御式风格一脉相承:Penpot 的智能体规程普遍遵循"先呈现可审查的证据,再执行有副作用的动作"。
Phase 3 — 执行:把文件编辑为约定好的合并结果
原文 Phase 3 只有一条规则(第 29–31 行):按方案把每个冲突文件编辑为双方商定的合并内容,删除所有冲突标记。
看似简单,但它是整条链路里唯一被允许修改文件内容的阶段。前两个阶段(只读分析、方案呈现)都是纯读操作,写操作被收敛到一个与人类批准严格挂钩的窗口内——这正是"Phase 2 末尾的等待"存在的意义:批准是写操作的许可证。
Phase 4 — 暂存与验证:用两条可机器检验的标准收尾
原文 Phase 4(第 33–36 行)给出两条硬性验证标准:
- 用
git add <file>暂存每一个已解决的冲突文件;不要顺手暂存与本次解决无关的 untracked 文件,除非它们明确属于解决方案的一部分; - 验证无残留:在已解决文件中搜索
<<<<<<</>>>>>>>,确认没有冲突标记残留;并且git status中不再出现 unmerged paths。
这两步都是低成本、可重复执行的自检,专门防御 LLM 常见失误——编辑时漏删一行 =======,或只解决了部分冲突文件就宣布完成。"不暂存无关文件"的补充规则则防止 Agent 把用户工作区里的其他未跟踪文件(草稿、临时输出)误打进暂存区,污染后续提交。注意这一步的终点是暂存(index)而非提交:git add 是可逆的本地状态变更,而 git rebase --continue 会推进 rebase 状态机并创建新的提交,二者对系统的影响量级不同,命令把分界线精确画在 git add 上。
Phase 5 — 汇报即止:把"收尾权"交还给用户
原文 Phase 5(第 38–41 行)要求:简要汇报冲突状态、每个冲突文件是如何解决的(以及 open questions 收到了什么答案),然后停下——不得运行 git rebase --continue 或任何类似的推进命令。
"汇报即止"与开头的禁令、frontmatter 的描述共同构成三重强调。从工作流角度看,收尾动作(git rebase --continue、处理可能的下一个冲突、最终 git status 确认)留给人类,意味着人类始终掌握 rebase 的推进节奏和最终判断——这与 AGENTS.md 中 "The user pushes from their own shell. If a push is required to surface the agent's work ... state this in the response and wait for the user" 的处理方式完全同构:Agent 做到自己权限的边界,把"下一步由谁触发"的控制权明确移交。
从源码结构看:这条命令与 Penpot 多工作区开发流程的衔接
从仓库结构看,这条命令不是孤立的提示词,而是嵌在 Penpot 一套智能体辅助开发设施里的:
- 多工作区 devenv:devenv 文档 描述了
ws0(主仓库状态)+ws1+(位于~/.penpot/penpot_workspaces/的克隆)的并行工作区模型,--sync会把主仓库状态复制到各工作区。冲突(rebase 进行中)会让主仓库处于"fragile state",此时所有 sync 被 manage.sh 的assert-clean-git-state阻断。因此"快速且正确地解决本地冲突"直接决定了并行工作区能否恢复同步——resolve-git-conflicts 命令处理的正是这条链路上的阻塞点; - 命令 → skill 的委托关系:
.opencode/commands/下的命令与.opencode/skills/下的 skill 是"流程"与"规范细节"的分工。例如 implement-plan.md 在提交步骤委托给 create-commit skill(该 skill 拥有提交格式、staging 审查与安全禁令,包括"不要运行git reset/git checkout/git clean/rm"这类破坏性命令);resolve-git-conflicts 则刻意不委托任何 skill,因为它的全部动作(读文件、git show、编辑、git add)都已在命令内定义完毕,无需额外的规范源; - 记忆系统的配合:AGENTS.md 要求 Agent 在特定动作前读取
mem:workflow/*记忆(如提交前读mem:workflow/creating-commits)。冲突解决不涉及提交,故该命令未挂接此类前置读取——这也从侧面印证其职责收敛在"文件级冲突消解 + 暂存"这一最小集内。
如何把这套规程搬进你自己的仓库
如果想在其他项目复用这个工作流,可以直接从原文的五个阶段抽象出一份可执行的冲突解决清单(保留原命令的全部约束,只补充了通用的命令细节):
1. 只读诊断
- git status # 确认 rebase/merge/cherry-pick 状态与冲突文件清单
- 读取每个 unmerged 文件,定位 <<<<<<< / ======= / >>>>>>>
- git show :ours:<file>、git show :theirs:<file> # 两侧干净版本
- git log / git show <sha> # 理解两侧提交的意图
2. 呈现方案(先批准,后动手)
- 每个文件:两侧各改了什么、为什么;建议方案与理由;组合方式
- 判据:纯新增冲突 → 两侧合并;同段代码竞争 → 保留语义正确版本,可辨识时融合双方意图
- 无法从代码/提交信息判定的决策 → 集中写入 "Open Questions" 待用户一次性作答
- 未获用户确认前:不编辑、不暂存
3. 执行
- 按批准的方案改写文件,删除全部冲突标记
4. 暂存与验证
- 对每个已解决文件执行 git add <file>(不碰无关 untracked 文件)
- grep 确认 <<<<<<< / >>>>>>> 无残留
- git status 确认无 unmerged paths
5. 汇报并停止
- 汇报冲突状态、每个文件的解决方式、open questions 的答案
- 不运行 git rebase --continue / --skip / git merge --continue,收尾由用户执行
这套清单的价值不在命令本身(它们都是基础 Git 操作),而在顺序与边界:只读诊断 → 方案评审 → 受控写入 → 机器可验证的收尾 → 控制权移交。对 AI Agent 而言,冲突解决最大的风险不是"不会合并",而是在错误理解意图的情况下把错误的合并一路推到 rebase --continue,使结果以提交的形式固化下来。Penpot 这条命令通过"批准前零副作用 + 止步于 git add + 三重红线强调"把这类风险压到了最低——这也是该文档对任何使用智能体辅助 Git 工作流的团队最有参考价值的部分。
参考路径
- 命令正文:.opencode/commands/resolve-git-conflicts.md
- 同目录配套命令:.opencode/commands/implement-plan.md、.opencode/commands/review.md
- 智能体硬规则:AGENTS.md
- 脆弱 Git 状态检测实现:manage.sh
- 多工作区与 fragile state 说明:docs/technical-guide/developer/devenv.md
- 提交规范 skill(staging 审查与安全禁令):.opencode/skills/create-commit/SKILL.md
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00