首页
/ Penpot implement-plan 命令详解:从 Issue 到 Commit 的 AI Agent 端到端实现流水线

Penpot implement-plan 命令详解:从 Issue 到 Commit 的 AI Agent 端到端实现流水线

2026-09-06 20:19:01作者:吴年前Myrtle

Penpot 仓库内置了一套面向 AI 编程 Agent(OpenCode)的自动化研发流程,其中 implement-plan 命令 是"计划已就绪"之后的执行入口:它在一次调用内串联起创建 GitHub Issue、创建 issue-NNNN 分支、按计划实现功能、按 Penpot 提交规范落盘 commit 四个环节。读完本文,你将掌握这条流水线的每一步设计意图、所依赖的 skill 与 workflow memory 的协作关系,以及 Penpot 为 Agent 设定的安全边界(如"永不 push"),并能在自己的仓库中参考同样的模式组织 AI 辅助开发流程。

命令定位:一次"计划就绪"后的全链路执行

implement-plan 的完整定义位于 .opencode/commands/implement-plan.md,其 YAML frontmatter 声明了它的基本属性:

description: Execute a ready plan end-to-end  create a GitHub issue, branch issue-NNNN, implement the plan, then commit via the create-commit skill
agent: build

两个关键点值得注意:

  • agent: build:该命令在具有写代码能力的 build agent 下运行(对照同目录的 resolve-git-conflicts.md 也使用 build agent,而 review.md 则明确要求"不修改任何代码")。
  • 无额外参数:文档原文明确写道——"This command is run once a plan is ready (for example, from plan mode). Execute the plan already prepared in the current session context — it does not take extra arguments." 也就是说,命令的全部输入来自当前会话上下文中已经准备好的计划,它不负责重新规划,只负责把计划端到端地执行完毕。

这与上游的 planner skill 形成清晰的职责切分:planner 扮演"只读的高级软件架构师"角色,产出包含 Context、Affected Modules、Approach、Risks、Testing 五个部分的结构化实施计划,并将其保存到 .opencode/plans/YYYY-MM-DD-<title>.md;而 implement-plan 接手之后的第一件事,就是把这个计划"物化"为一个可追踪的 Issue,再围绕 Issue 组织分支与提交。整个链条可以概括为:

planner(只读规划)
   └─> implement-plan 命令
         ├─ 1. create-issue skill  → 获得 Issue 编号 NNNN
         ├─ 2. git checkout -b issue-NNNN
         ├─ 3. 按会话中的计划实现代码(不提交)
         └─ 4. create-commit skill → 规范 commit,不 push

步骤 1:创建 Issue —— 把计划转成可追踪的研发单元

命令的第一步是"Use the create-issue skill, following the Creating Issues from Draft Body flow in mem:workflow/creating-issues. Derive the issue title and body from the plan." 这一步有两条硬性要求:

  1. 标题与正文必须从计划派生,而不是直接复用计划文件名;
  2. 必须捕获新 Issue 的编号,文档把它记为 NNNN——因为这个编号同时决定分支名和后续 commit 的引用格式,是整条流水线的"主键"。

create-issue skill 本身只是一个薄入口(见 create-issue/SKILL.md),所有真实规则——标题派生、元数据策略、正文模板、Issue Type ID——都集中存放在 workflow/creating-issues.md 这份 workflow memory 中,implement-plan 指定的正是其中的 "Creating Issues from Draft Body" 流程(没有现成 PR,从计划/草稿正文出发建 Issue)。这份 memory 提供了相当具体的可执行细节,构成 implement-plan 第 1 步的完整操作手册:

标题派生规则

  • Bug 标题用描述性现在时,格式为 [Where] [present-tense verb] when [condition],例如 "Plugin API crashes when setting text fills";禁止以 "Fix" 等祈使动词开头。
  • Feature / Enhancement 标题用祈使句,格式为 [Imperative verb] [what] in/on [where],例如 "Add customizable dash and gap length controls to dashed strokes in the sidebar"。
  • 通用规则:必须写清"where"(UI 位置或模块);剥离 bug:feature::bug: 等前缀;纯文本、不用 emoji;一个描述里若含两个相关问题,用 "and" 把两者都写进标题。

元数据与正文模板

字段 规则
Labels 社区贡献者 PR 加 community contribution;不加 bug/enhancement 标签(用 Issue Type 代替)
Milestone 当前或下一个计划里程碑,不确定则省略
Project 固定为 Main(project number 8),用 --project "Main"
Issue Type gh issue create 无法直接设置,需创建后用 GraphQL 补设

正文则写入临时文件以避免 shell 引号问题,Bug 与 Enhancement 各有固定模板(Description / Steps to reproduce / Expected behavior / Affected versions,或 Description / Use case / Affected versions),并且要求不要软换行段落——每段在源码中保持单行,换行只用于结构性分隔(章节标题、列表项、代码围栏、空行),以便后续 diff 更干净。

建 Issue 的实际命令

cat > /tmp/issue-body.md << 'ISSUE_BODY'
<body content here>
ISSUE_BODY

gh issue create \
  --repo penpot/penpot \
  --title "<Derived title>" \
  --label "<label>" \
  --project "Main" \
  --body-file /tmp/issue-body.md

命令输出形如 https://github.com/penpot/penpot/issues/<NUMBER>,这里的 <NUMBER> 就是 implement-plan 文档所说的 NNNN。若需要设置 Issue Type,则先通过 GraphQL 查询拿到 Issue 的 node id,再执行 updateIssue mutation 设置 issueTypeId(memory 中列出了 penpot/penpot 仓库六种 Issue Type 的 ID 表,以及"Bug report → Bug、feature request → Enhancement/Feature、文档 → Docs、其余 → Task"的映射规则),最后用 gh issue view <NUMBER> --json ... 验证标题、标签、里程碑与项目归属。

步骤 2:创建以 Issue 命名的分支

Issue 创建成功后,命令要求立即切换到以 Issue 命名的分支:

git checkout -b issue-NNNN

其中 NNNN 替换为步骤 1 捕获的 Issue 编号。这个命名约定让分支与其追踪的 Issue 一一对应:从分支名即可反查该次改动对应的需求单元,也为后续 commit 中的 Closes #NNNN 引用建立了可校验的锚点。值得注意的是,implement-plan 只要求"创建并切换",而不要求任何 push 或远端操作——这与 AGENTS.md 顶部的 HARD RULES 一致:"Never git push, force-push, or modify git origin... The user pushes from their own shell." Agent 的全部活动被限制在本地工作区内。

步骤 3:执行计划 —— 只实现,不提交

第三步是流水线的主体:"Implement the prepared plan from the session context. Work methodically, keeping changes focused on what the issue requires. Do not commit — the commit happens in step 4."

这里有两层设计意图:

  • 聚焦实现:改动严格限定在 Issue 所要求的内容上。上游 planner 在生成计划时已经按"垂直切片"原则把任务拆成 XS/S/M 级(每个任务不超过约 5 个文件)、附带验收标准与验证命令,并依据 monorepo 依赖图(frontend -> commonbackend -> commonexporter -> commonfrontend -> render-wasm)排定顺序,执行阶段要做的就是逐任务落地并逐段验证。
  • 提交权后置:实现阶段禁止 git commit,把"何时提交、如何写 message"收敛到第 4 步的单一入口。这避免了实现中途产生碎片化提交,也保证最终提交信息能一次性准确概括全部改动。

同时,AGENTS.md 要求 Agent 在编码前先读受影响模块的 core memory(如 frontend/corebackend/corecommon/core),并"Never pipe test output directly to filters"——测试输出必须先重定向到文件再检查,以防隐藏失败。这些约束虽然不写在 implement-plan 文档内,却是该命令执行环境的一部分。

步骤 4:通过 create-commit skill 落盘提交

实现完成后,命令要求"load the create-commit skill and follow its workflow to commit the changes",并特别指出要提供三样东西:

  1. 一段简要总结——实现了什么、为什么
  2. Issue 引用(issue-NNNN);
  3. 当前运行的模型名称,以便正确写入 AI-assisted-by trailer。

create-commit skill 的职责边界写得很清楚:它"owns the commit format, staging review, and safety checks — it does not implement features or push"。其工作流为:

  1. 暂存调用上下文指定的文件(不向用户确认);
  2. 运行 git diff --staged 复查内容——若发现密钥、.env 值、调试打印或与声明意图不符的内容,立即停下并告知用户
  3. 按规范起草 message(正文每行 72 字符换行),执行 git commit -m "<subject>" -m "<body>"(正文含特殊字符时可用 git commit -F -);
  4. AI-assisted-by trailer 的取值由调用上下文提供,原样使用

commit message 的完整规范在 workflow/creating-commits.md 中定义:

:emoji: Subject line (imperative, capitalized, no period, <=70 chars)

Body explaining what changed and why.
Wrap lines at 72 characters — git log and tooling
render long lines poorly. Keep each line concise.

AI-assisted-by: model-name

要点包括:

  • Emoji 类型菜单:bug: bug fix、:sparkles: enhancement、:tada: new feature、:recycle: refactor、:lipstick: cosmetic、:ambulance: critical fix、:books: docs、:wrench: config、:zap: perf、:whale: docker、:fire: removal、:globe_with_meridians: translations 等共 17 种。
  • Issue 引用格式:使用 Closes #NNNN(而不是 Fixes #NNNN)将 commit 关联到 GitHub Issue。
  • AI-assisted-by 规则:只写裸模型名(如 mimo-v2.5),不加 opencode-go/ 之类前缀。
  • 身份来源:不猜测、不使用 --author,作者身份取自本地 git config。

create-commit 还附带一组硬约束:不 push、不运行 git reset/git checkout/git restore/git clean/rm、不改作者身份、未经明确要求不 amend 本会话之外的提交、不绕过 pre-commit hooks、不添加本会话未创建的未跟踪文件。

安全边界:流水线在哪里"停下"

把整条命令串起来看,implement-plan 的终点是本地的一次规范 commit,其最后一句明确写道:"Do not push. Pushing is handled separately by the user." 即推送动作永远留给用户在自己的 shell 中完成。这一设计与 AGENTS.md 的硬规则(永不 push、永不修改远端、已推送的 commit 未经明确要求不 amend)共同构成了 Agent 的版本控制安全边界:Agent 负责 issue 化、分支、实现与提交这些可本地验证的环节,而所有影响远端仓库的操作都保留给人类。

小结:四个环节与它们的支撑文件

环节 动作 关键支撑
上游 生成结构化实施计划(只读) planner skill、计划存至 .opencode/plans/
1 从计划派生 Issue,捕获编号 NNNN create-issue skillcreating-issues memory
2 git checkout -b issue-NNNN 分支名与 Issue 一一对应
3 按会话中的计划实现,不提交 计划中的任务切片、验收标准与模块验证命令
4 按规范提交(emoji 前缀、72 列正文、Closes #NNNNAI-assisted-by),不推送 create-commit skillcreating-commits memory

implement-plan 的价值在于它把"AI 写代码"中最容易失控的环节——需求追踪(Issue)、隔离(分支)、质量收敛(单次规范提交)、责任边界(不 push、不篡改作者身份)——固化成一条不可跳步的流水线,每一步的格式细节都下沉到可独立维护的 skill 与 memory 中。对于希望在自己仓库中搭建类似 AI 辅助研发流程的团队,这套"command 只做编排、规则下沉到 skill/memory、推送权保留给人类"的划分方式是一个值得直接参照的模板。

登录后查看全文
热门项目推荐
相关项目推荐