issue-flow

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Issue Flow

Issue 流程

Manages the full GitHub issue lifecycle: select → issue → branch → commits → PR → merge → close. Supports entering at any step and advancing forward. Reads the target repo's CLAUDE.md for project-specific conventions.
This skill owns every git and
gh
write
in the issue pipeline — branch creation, snapshots, commits, squashing, pushes, PRs, merges — plus issue selection. Skills that orchestrate the pipeline (
solve-issue
) delegate those actions here rather than reimplementing them, so the commit subject format and the squash rules exist in exactly one place.
管理完整的GitHub issue生命周期:选择→创建issue→创建分支→提交代码→创建PR→合并PR→关闭issue。支持从任意步骤切入并推进流程。会读取目标仓库的CLAUDE.md文件以遵循项目特定规范。
该技能管控issue流程中所有git和
gh
的写入操作
——包括分支创建、快照、提交、合并提交、推送、创建PR、合并PR——以及issue选择。编排流程的技能(如
solve-issue
)会将这些操作委托给本技能,而非重新实现,因此提交主题格式和合并规则仅在一处定义。

Bundled Scripts

内置脚本

Located in
scripts/
relative to this skill:
ScriptPurpose
check-env.sh
Validate git repo, gh CLI, authentication
detect-base.sh
Detect base branch name (main/master/next/epic-*)
repo-context.sh
Fetch labels, collaborators, projects
repo-ownership.sh
Classify repo as personal / org / external
pr-reviewers.sh
Rank top PR reviewers by recent review activity
issue-assignees.sh
Rank top issue assignees by recent assignments
add-to-project.sh
Add issue to GitHub Projects V2
get-issue-projects.sh
List projects an issue is already a member of
suggest-projects.sh
Rank up to 4 likely projects (USED + RELATED)
project-status.sh
Update project board status
detect-base.sh
prints a branch name, not a rev. An epic branch can exist only on the remote, so
git log <base>..HEAD
fails with
unknown revision
on a name that has no local branch. Use it as-is where a name is wanted (
git checkout
,
git pull origin
,
gh pr create --base
); resolve it first wherever a rev is wanted:
bash
baseref=$(git rev-parse --verify --quiet "origin/$base" || git rev-parse --verify --quiet "$base")
It exits
2
when the repo has no remote, in which case there is no base to detect — stop and tell the user to add a remote rather than guessing
main
.
The name can also be missing in the other direction: it scans local
epic-*
branches too, so the base may be a branch the remote has never seen. Anything that talks to the remote with it —
git pull origin <base>
,
git rebase origin/<base>
,
gh pr create --base <base>
— fails on such a base. Check before those steps and stop with
Base branch <base> is not on the remote — push it first.
Detection is still right to find it: falling back to the default branch would make Consolidate treat the whole epic as commits ahead and squash it.
Run scripts from the skill directory:
bash
bash "<skill-dir>/scripts/check-env.sh"
bash "<skill-dir>/scripts/detect-base.sh"
bash "<skill-dir>/scripts/repo-context.sh"
bash "<skill-dir>/scripts/repo-ownership.sh" [<owner>/<repo>]
bash "<skill-dir>/scripts/pr-reviewers.sh" [<owner>/<repo>] [<limit>]
bash "<skill-dir>/scripts/issue-assignees.sh" [<owner>/<repo>] [<limit>]
bash "<skill-dir>/scripts/add-to-project.sh" <issue-number> <project-title> [status]
bash "<skill-dir>/scripts/get-issue-projects.sh" <issue-number>
bash "<skill-dir>/scripts/suggest-projects.sh" [<owner>/<repo>]
bash "<skill-dir>/scripts/project-status.sh" <issue-number> <status>
位于本技能相对路径的
scripts/
目录下:
脚本名称用途
check-env.sh
验证git仓库、gh CLI及身份认证状态
detect-base.sh
检测基础分支名称(main/master/next/epic-*)
repo-context.sh
获取标签、协作者、项目信息
repo-ownership.sh
将仓库分类为个人仓库/组织仓库/外部仓库
pr-reviewers.sh
根据近期评审活跃度排序顶级PR评审人
issue-assignees.sh
根据近期分配情况排序顶级issue经办人
add-to-project.sh
将issue添加至GitHub Projects V2
get-issue-projects.sh
列出issue已加入的项目
suggest-projects.sh
排序最多4个潜在项目(已使用+相关项目)
project-status.sh
更新项目看板状态
detect-base.sh
输出的是分支名称,而非版本号。epic分支可能仅存在于远程仓库,因此在本地没有对应分支的情况下,
git log <base>..HEAD
会因
unknown revision
报错。当需要分支名称时(如
git checkout
git pull origin
gh pr create --base
)直接使用该输出;当需要版本号时,先解析分支名称:
bash
baseref=$(git rev-parse --verify --quiet "origin/$base" || git rev-parse --verify --quiet "$base")
当仓库没有远程仓库时,该脚本会以状态码2退出,此时无法检测基础分支——需提示用户添加远程仓库,而非默认使用
main
分支。
另一种情况是分支名称仅存在于本地:脚本也会扫描本地的
epic-*
分支,因此检测到的基础分支可能并未推送到远程仓库。若使用该分支执行与远程交互的操作——如
git pull origin <base>
git rebase origin/<base>
gh pr create --base <base>
——会执行失败。在执行这些步骤前需检查并提示用户:
Base branch <base> is not on the remote — push it first.
检测逻辑是正确的,若回退到默认分支,会导致Consolidate将整个epic分支的提交视为超前并合并。
从技能目录运行脚本:
bash
bash "<skill-dir>/scripts/check-env.sh"
bash "<skill-dir>/scripts/detect-base.sh"
bash "<skill-dir>/scripts/repo-context.sh"
bash "<skill-dir>/scripts/repo-ownership.sh" [<owner>/<repo>]
bash "<skill-dir>/scripts/pr-reviewers.sh" [<owner>/<repo>] [<limit>]
bash "<skill-dir>/scripts/issue-assignees.sh" [<owner>/<repo>] [<limit>]
bash "<skill-dir>/scripts/add-to-project.sh" <issue-number> <project-title> [status]
bash "<skill-dir>/scripts/get-issue-projects.sh" <issue-number>
bash "<skill-dir>/scripts/suggest-projects.sh" [<owner>/<repo>]
bash "<skill-dir>/scripts/project-status.sh" <issue-number> <status>

Step Router

步骤路由

Before routing, detect current state in parallel:
bash
undefined
路由前,并行检测当前状态:
bash
undefined

Run all three in parallel (independent calls)

并行执行三个独立命令

git branch --show-current # → current branch name git status --short # → working tree state (staged, unstaged, untracked) git log --oneline -1 # → latest commit context

Use the current branch name to determine which steps are already done:
- On `issue-<N>` branch → Steps 1-2 are done, detect entry from there
- On base branch (main/master/etc.) with no changes → nothing to do
- On base branch with changes → full flow from Step 1

Determine entry step from user intent, check prerequisites, then proceed forward. Do NOT re-run earlier completed steps.

| User intent                          | Entry step | Prerequisite             |
| ------------------------------------ | ---------- | ------------------------ |
| "what's next", "pick an issue", "which issue" | Step 0 | gh authenticated       |
| Work intent with no issue number given | Step 0  | gh authenticated         |
| No arguments / empty invocation      | Step 1     | Staged or changed files  |
| "create issue", "new issue"          | Step 1     | gh authenticated         |
| "create issue linked to #N", "create sub-issue of #N" | Step 1 + Project Inheritance | Parent #N exists |
| "add sub-issues to #N", "link issues to #N" | Sub-issues | Parent issue exists |
| "X blocks #N", "block #N with #M", "unblock #N" | Blocked-by | Both issues exist |
| "start work on #N", "branch for #N"  | Step 2     | Issue exists             |
| "commit", "commit changes"           | Step 3     | On issue-* branch        |
| "snapshot", "wip snapshot"           | Step 3 snapshot | Dirty tree        |
| "push", "push changes"               | Step 4     | Commits ahead of remote  |
| "create PR", "open PR"               | Step 5     | Branch pushed            |
| "merge", "merge PR"                  | Step 6     | PR exists                |
git branch --show-current # → 当前分支名称 git status --short # → 工作区状态(已暂存、未暂存、未跟踪) git log --oneline -1 # → 最新提交上下文

根据当前分支名称判断已完成的步骤:
- 处于`issue-<N>`分支 → 已完成步骤1-2,从该节点切入
- 处于基础分支(main/master等)且无变更 → 无需操作
- 处于基础分支且有变更 → 从步骤1开始完整流程

根据用户意图确定切入步骤,检查前置条件后推进流程。**不要重新执行已完成的步骤**。

| 用户意图                                  | 切入步骤 | 前置条件                 |
| ----------------------------------------- | -------- | ------------------------ |
| "下一步做什么"、"选择一个issue"、"选哪个issue" | 步骤0 | gh已完成身份认证         |
| 未指定issue编号的工作意图                 | 步骤0  | gh已完成身份认证         |
| 无参数调用/空调用                         | 步骤1     | 存在已暂存或变更的文件   |
| "创建issue"、"新建issue"                  | 步骤1     | gh已完成身份认证         |
| "创建关联#N的issue"、"创建#N的子issue" | 步骤1 + 项目继承 | 父issue #N存在 |
| "给#N添加子issue"、"将issue关联到#N" | 子issue流程 | 父issue存在 |
| "X阻塞#N"、"用#M阻塞#N"、"取消阻塞#N" | 依赖关系设置 | 两个issue均存在 |
| "开始处理#N"、"为#N创建分支"              | 步骤2     | issue已存在              |
| "提交"、"提交变更"                       | 步骤3     | 处于issue-*分支          |
| "快照"、"WIP快照"                       | 步骤3(快照模式) | 工作区有未提交变更 |
| "推送"、"推送变更"                       | 步骤4     | 本地提交超前于远程仓库   |
| "创建PR"、"打开PR"                       | 步骤5     | 分支已推送到远程仓库     |
| "合并"、"合并PR"                         | 步骤6     | PR已存在                 |

No Arguments (Full Flow from Changes)

无参数调用(从变更开始完整流程)

When invoked without arguments, use the state detected above:
  1. If
    git status --short
    shows no output, stop — nothing to commit
  2. If there are changes, analyze the diff content to infer issue type and description
  3. Run the full flow (Steps 1–6) — stage only the identified files in Step 3
When the user says "full flow" or asks to go from issue to merge, run all steps sequentially. Otherwise, start at the detected step and ask whether to continue to the next step after each one completes.
无参数调用时,根据上述检测到的状态执行:
  1. git status --short
    无输出,停止操作——无内容可提交
  2. 若存在变更,分析diff内容推断issue类型和描述
  3. 执行完整流程(步骤1–6)——步骤3中仅暂存已识别的文件
当用户要求"完整流程"或从issue到合并的全流程时,按顺序执行所有步骤。否则从检测到的步骤开始,每完成一步询问用户是否继续下一步。

Conventions

规范

Read the target repo's CLAUDE.md for project-specific formatting. Use these defaults when no override is found:
  • Issue titles:
    <type>: <description>
    (conventional commit format)
  • Commit subjects:
    <Issue Title> #<number>
  • PR titles:
    <Issue Title> #<number>
  • PR body: concise change list +
    Closes #<number>
    , one per line. GitHub links only the first reference after a keyword, so
    Closes #1 #2 #3
    closes #1 and leaves #2 and #3 open
Common types:
feat
,
fix
,
docs
,
chore
,
refactor
,
test
,
style
,
ci
读取目标仓库的CLAUDE.md文件以遵循项目特定格式。若未找到自定义规范,使用以下默认规则:
  • Issue标题
    <type>: <description>
    (规范提交格式)
  • 提交主题
    <Issue Title> #<number>
  • PR标题
    <Issue Title> #<number>
  • PR正文:简洁的变更列表 +
    Closes #<number>
    ,每行一条。GitHub仅会关联关键字后的第一个引用,因此
    Closes #1 #2 #3
    只会关闭#1,#2和#3仍会保持打开状态
常见类型:
feat
fix
docs
chore
refactor
test
style
ci

Asking the User

询问用户

Every question in this skill is written as
AskUserQuestion
options. Use that tool where the host offers it, or the host's nearest structured-choice equivalent. Where the host has neither, ask the same question in normal chat as a numbered list of 2–5 options — recommended first, one short line of description each — and wait for the user to reply with a number.
Never silently pick for the user at a gate that changes git or GitHub state.
本技能中的所有问题均以
AskUserQuestion
选项形式呈现。若宿主支持该工具则直接使用,或使用宿主提供的最接近的结构化选择工具。若宿主均不支持,则在聊天中以编号列表形式呈现2-5个选项——推荐选项排在首位,每个选项配简短描述——等待用户回复编号。
在会修改git或GitHub状态的节点,切勿擅自替用户做选择。

Skip Interactive Prompts

跳过交互式提示

When the user explicitly provides values for labels, assignee, project, or other options in their request, use those values directly — do NOT ask to confirm what was already stated. Only ask about fields the user left unspecified.
当用户在请求中明确指定标签、经办人、项目或其他选项的值时,直接使用这些值——不要询问确认已明确提供的内容。仅询问用户未指定的字段。

Assignment Defaults

默认分配规则

Nothing this skill creates is ever left unassigned by default. Every issue and PR gets an assignee unless the user explicitly asked for none.
"The current user" always means the authenticated
gh
account — resolve it, never assume a hardcoded login:
bash
gh api user --jq .login
Prefer the literal
@me
in
gh
flags; use the resolved login only when a report or comparison needs the actual name.
Classify the repo once per flow — at whichever step the flow is entered, if not already done:
bash
bash "<skill-dir>/scripts/repo-ownership.sh"
KIND
drives the default:
KIND
MeaningDefault behaviour
personal
Owned by the current userAssign the current user (
@me
) silently — do not prompt
org
Owned by an organizationAsk via
AskUserQuestion
; the current user is the recommended default
external
Another user's personal repoSame as
org
— ask, current user recommended
In every case the fallback is the current user: if the user skips the question, the prompt cannot be shown, or
repo-ownership.sh
fails, assign
@me
rather than nothing.
An explicit instruction always wins — "assign to @octocat", "leave it unassigned", or a reviewer/assignee rule in the target repo's CLAUDE.md overrides everything above, in personal repos too.
本技能创建的所有内容默认不会无人分配。除非用户明确要求,否则每个issue和PR都会分配经办人。
"当前用户"始终指已认证的
gh
账户——需解析该账户,切勿假设硬编码的登录名:
bash
gh api user --jq .login
gh
参数中优先使用字面量
@me
;仅在报告或比较需要实际名称时使用解析后的登录名。
每个流程仅分类一次仓库——无论从哪个步骤切入,若尚未分类则执行:
bash
bash "<skill-dir>/scripts/repo-ownership.sh"
KIND
值决定默认行为:
KIND
含义默认行为
personal
当前用户拥有的仓库静默分配给当前用户(
@me
)——提示用户
org
组织拥有的仓库通过
AskUserQuestion
询问用户;当前用户为推荐默认值
external
其他用户的个人仓库
org
仓库相同——询问用户,推荐当前用户
在任何情况下,回退选项都是当前用户:若用户跳过问题、无法显示提示或
repo-ownership.sh
执行失败,分配
@me
而非留空。
明确指令优先级最高——"分配给@octocat"、"不分配经办人"或目标仓库CLAUDE.md中的评审人/经办人规则会覆盖上述所有规则,包括个人仓库的默认规则。

Step 0: Select Issue

步骤0:选择Issue

Use when no issue number was given and the intent is to work on something — "what's next", "pick an issue", or a bare work intent with nothing to work on named.
Validate the environment and resolve the repo first:
bash
bash "<skill-dir>/scripts/check-env.sh"
gh repo view --json nameWithOwner --jq '.nameWithOwner'
gh api user --jq .login
If
gh repo view
fails, stop:
Not inside a GitHub repository.
Then run the ranking pipeline in
references/issue-selection.md
. It reads local git state, plan files, open PRs, and the backlog, ranks candidates into five tiers, and presents up to four picks via
AskUserQuestion
. Every command it runs is a read — Step 0 never writes.
On selection it hands off to the
issue-analyze
skill for the full implementation analysis. Continue to Step 2 with the selected number when the flow is meant to keep going; stop after the analysis when the user only asked what to work on next.
If the user picks
None
, stop — do not fall through to Step 1 and create an issue nobody asked for.
当未指定issue编号且用户意图是开始工作时使用——如"下一步做什么"、"选择一个issue"或未指定工作内容的工作意图。
首先验证环境并解析仓库:
bash
bash "<skill-dir>/scripts/check-env.sh"
gh repo view --json nameWithOwner --jq '.nameWithOwner'
gh api user --jq .login
gh repo view
执行失败,停止操作:
Not inside a GitHub repository.
然后运行
references/issue-selection.md
中的排序流程。该流程读取本地git状态、计划文件、打开的PR和待办事项,将候选issue分为五个层级,并通过
AskUserQuestion
呈现最多4个选项。该步骤仅执行读取操作——步骤0不会写入任何内容。
用户选择后,将任务交给
issue-analyze
技能进行完整的实现分析。若流程需要继续推进,则携带选中的issue编号进入步骤2;若用户仅询问下一步工作内容,则分析完成后停止。
若用户选择
None
,停止操作——不要自动进入步骤1创建无人需要的issue。

Step 1: Create Issue

步骤1:创建Issue

Run these in parallel (they are independent):
bash
undefined
并行执行以下操作(相互独立):
bash
undefined

Parallel batch

并行执行

bash "<skill-dir>/scripts/check-env.sh" mktemp -d # → save output as <TMPDIR> bash "<skill-dir>/scripts/repo-context.sh" bash "<skill-dir>/scripts/repo-ownership.sh"

Wait for all four before proceeding. `detect-base.sh` is not needed until Step 2.
bash "<skill-dir>/scripts/check-env.sh" mktemp -d # → 保存输出为<TMPDIR> bash "<skill-dir>/scripts/repo-context.sh" bash "<skill-dir>/scripts/repo-ownership.sh"

等待四个命令执行完成后再继续。`detect-base.sh`需到步骤2才会用到。

Title

标题

Use conventional commit format:
<type>: <description>
. Defer to CLAUDE.md conventions if they differ.
使用规范提交格式:
<type>: <description>
。若CLAUDE.md中有不同规范,优先遵循该规范。

Epic Detection

Epic检测

Before writing the body or picking a label, determine if this is an epic issue — one that coordinates work without containing implementation itself. Signals:
  • User mentions "epic", "umbrella", "tracking issue", or "aggregated issue"
  • Issue groups multiple child issues or feature areas
  • No concrete implementation details — only scope or coordination
If it is an epic, and the repo has an
epic
label (check
repo-context.sh
output), apply
epic
as the label without asking. Skip the normal type-based label inference. If
epic
label does not exist in the repo, fall through to normal label selection.
在编写正文或选择标签前,判断该issue是否为epic issue——即用于协调工作但不包含具体实现的issue。判断信号:
  • 用户提及"epic"、"总览"、"跟踪issue"或"汇总issue"
  • issue包含多个子issue或功能模块
  • 无具体实现细节——仅包含范围或协调内容
若为epic issue,且仓库存在
epic
标签(检查
repo-context.sh
输出),则自动添加
epic
标签,无需询问用户。跳过常规的基于类型的标签推断。若仓库不存在
epic
标签,执行常规标签选择流程。

Body

正文

Write a brief 2-4 sentence description. No markdown headers.
For epic issues: do NOT list child issue numbers in the body. Sub-issue relationships are managed via the GitHub sub-issues API (see ## Sub-Issues), not via body text.
编写2-4句话的简短描述,不使用markdown标题。
对于epic issue:不要在正文中列出子issue编号。子issue关系通过GitHub子issue API管理(见**## 子Issue**),而非正文文本。

Labels

标签

Auto-detect label from the issue type (e.g.,
feat
feature
or
enhancement
). Match against labels fetched by
repo-context.sh
. Use
AskUserQuestion
to confirm with the user — show top 3 matching labels + "No label".
根据issue类型自动检测标签(如
feat
feature
enhancement
)。匹配
repo-context.sh
获取的标签列表。通过
AskUserQuestion
确认用户选择——展示排名前3的匹配标签 + "无标签"选项。

Assignee

经办人

Follow ### Assignment Defaults. The issue always gets an assignee unless the user explicitly asked for none.
KIND=personal
— assign
@me
via
--assignee "@me"
without prompting. Skip
issue-assignees.sh
entirely.
KIND=org
or
KIND=external
— run
issue-assignees.sh
to find users with actual recent assignment activity:
bash
bash "<skill-dir>/scripts/issue-assignees.sh"
Output:
<user>\t<count>
per row, up to 3 rows (excludes self and bots). Compose
AskUserQuestion
:
  1. @me
    (Recommended)
  2. First result, description:
    "Assigned to <count> of last 100 issues"
  3. Second result, description:
    "Assigned to <count> of last 100 issues"
  4. "No assignee"
If
issue-assignees.sh
returns nothing, show only
@me
and "No assignee". Do not fall back to the generic
repo-context.sh
collaborator list — those are repo members ranked by nothing meaningful, and inventing labels like "Frequent collaborator" misleads the user.
If the question is skipped or unanswered, default to
@me
— never create the issue with no assignee.
遵循**### 默认分配规则**。除非用户明确要求,否则issue必须分配经办人。
KIND=personal
——通过
--assignee "@me"
静默分配给当前用户,跳过
issue-assignees.sh
KIND=org
KIND=external
——运行
issue-assignees.sh
查找近期有实际分配记录的用户:
bash
bash "<skill-dir>/scripts/issue-assignees.sh"
输出格式:每行
<user>\t<count>
,最多3行(排除自己和机器人)。构造
AskUserQuestion
选项:
  1. @me
    (推荐)
  2. 第一个结果,描述:
    "最近100个issue中被分配<count>次"
  3. 第二个结果,描述:
    "最近100个issue中被分配<count>次"
  4. "无经办人"
issue-assignees.sh
无输出,仅展示
@me
和"无经办人"选项。不要回退到
repo-context.sh
获取的通用协作者列表——这些仓库成员没有有意义的排序,使用"频繁协作者"等标签会误导用户。
若用户跳过或未回答问题,默认分配
@me
——切勿创建无经办人的issue。

Type

类型

Issue types are an organization-level feature. Probe for them:
bash
gh api "repos/<owner>/<repo>/issue-types" --jq '.[].name' 2>/dev/null || true
  • Names returned → map the conventional commit type onto the closest one and pass
    --type "<name>"
    to
    gh issue create
    .
  • 404 Not Found
    → the repo has no issue types. This is the normal answer for a personal repo, since only orgs define them. Skip silently.
Do not probe with
gh issue create --type bug --dry-run
. There is no
--dry-run
flag on
gh issue create
; it fails with
unknown flag
regardless of whether the repo supports types, so the probe always reports "unsupported".
Issue类型是组织级功能。检测仓库是否支持:
bash
gh api "repos/<owner>/<repo>/issue-types" --jq '.[].name' 2>/dev/null || true
  • 返回名称列表 → 将规范提交类型映射到最接近的类型,并在
    gh issue create
    中传递
    --type "<name>"
    参数。
  • 返回
    404 Not Found
    → 仓库不支持issue类型。个人仓库通常不支持该功能,因为只有组织可以定义。静默跳过该步骤。
不要使用
gh issue create --type bug --dry-run
检测。
gh issue create
没有
--dry-run
参数,无论仓库是否支持类型,该命令都会因
unknown flag
失败,导致检测结果始终为"不支持"。

Project

项目

If creating a child of an existing parent (linked, attached, or sub-issue of #N), follow ## Project Inheritance From Parent instead.
Otherwise, rank candidates via
suggest-projects.sh
(output:
<bucket>\t<id>\t<title>\t<note>
per row;
USED
= projects from your recent issues,
RELATED
= other active projects):
bash
bash "<skill-dir>/scripts/suggest-projects.sh"
Compose
AskUserQuestion
: slot 1 (Recommended) = first row, slots 2–4 = next rows, with
<note>
as each option's description. Backfill the last slot with "No project" when fewer than 4 rows exist; skip silently when zero rows. On selection, run
add-to-project.sh <issue-number> "<project-title>"
.
若创建现有父issue的子issue(关联、附加或属于#N的子issue),则遵循**## 从父Issue继承项目**规则。
否则,通过
suggest-projects.sh
排序候选项目(输出格式:每行
<bucket>\t<id>\t<title>\t<note>
USED
=你近期issue所在的项目,
RELATED
=其他活跃项目):
bash
bash "<skill-dir>/scripts/suggest-projects.sh"
构造
AskUserQuestion
选项:第1位(推荐)=第一行,第2-4位=后续行,每行的
<note>
作为选项描述。若不足4行,最后一个选项填充"无项目";若无输出则静默跳过。用户选择后,运行
add-to-project.sh <issue-number> "<project-title>"

Milestone

里程碑

Trigger: Check milestones when either:
  • The user explicitly asks to assign a milestone
  • The target repo's CLAUDE.md mentions milestones (any mention — section headers, instructions, config)
If neither trigger matches, skip silently.
When triggered, fetch open milestones:
bash
gh api "repos/<owner>/<repo>/milestones?state=open" --jq '.[] | {number, title, state, open_issues, closed_issues, due_on}'
Then apply:
  • 0 milestones: skip silently
  • 1 milestone: assign automatically — inform the user which milestone was used
  • 2+ milestones: use
    AskUserQuestion
    to pick one:
Which milestone for this issue?
1. "<title1>" (Recommended) — X open / Y closed, due YYYY-MM-DD
2. "<title2>" — X open / Y closed, due YYYY-MM-DD
3. No milestone
Order by due date (soonest first). The first non-closed milestone is recommended.
Assign via
--milestone "<title>"
in the
gh issue create
command.
触发条件:满足以下任一条件时检查里程碑:
  • 用户明确要求分配里程碑
  • 目标仓库的CLAUDE.md提及里程碑(任何提及——章节标题、说明、配置)
若两个触发条件均不满足,静默跳过该步骤。
触发后,获取开放的里程碑:
bash
gh api "repos/<owner>/<repo>/milestones?state=open" --jq '.[] | {number, title, state, open_issues, closed_issues, due_on}'
然后执行:
  • 0个里程碑:静默跳过
  • 1个里程碑:自动分配——告知用户使用的里程碑
  • 2个及以上里程碑:通过
    AskUserQuestion
    选择:
该issue分配哪个里程碑?
1. "<title1>"(推荐)—— X个开放 / Y个已关闭,截止日期YYYY-MM-DD
2. "<title2>"—— X个开放 / Y个已关闭,截止日期YYYY-MM-DD
3. 无里程碑
按截止日期排序(最近的优先)。第一个非关闭状态的里程碑为推荐选项。
gh issue create
命令中通过
--milestone "<title>"
参数分配里程碑。

Create

创建

Use the
<TMPDIR>
from the parallel setup batch. Write the issue body to
<TMPDIR>/body.md
with the host's file-write tool, then create the issue with
--body-file
:
bash
gh issue create --title "<title>" --body-file <TMPDIR>/body.md --label "<label>" --assignee "<assignee>" [--milestone "<name>"]
When creating multiple issues, use unique filenames per issue:
<TMPDIR>/<slug>-body.md
(e.g.
auth-body.md
,
settings-body.md
). Resolve
<TMPDIR>
once and reuse it for all issues.
Important: Replace
<TMPDIR>
with the literal absolute path in all commands (e.g.
--body-file /var/folders/.../issue-flow-AbCdEf/body.md
). Do NOT set
TMPDIR=
as an env var prefix on commands — that changes the command pattern and triggers permission prompts.
Do NOT use
--body "$(cat <<'EOF'...)"
— the
$()
command substitution makes the command unmatchable against any pre-approval rule, so hosts that gate shell commands re-prompt every time.
Print the Step 1 report (see
references/report-format.md
).
使用并行设置步骤中获取的
<TMPDIR>
。通过宿主的文件写入工具将issue正文写入
<TMPDIR>/body.md
,然后使用
--body-file
参数创建issue:
bash
gh issue create --title "<title>" --body-file <TMPDIR>/body.md --label "<label>" --assignee "<assignee>" [--milestone "<name>"]
创建多个issue时,每个issue使用唯一的文件名:
<TMPDIR>/<slug>-body.md
(如
auth-body.md
settings-body.md
)。仅解析一次
<TMPDIR>
并复用。
重要提示:将所有命令中的
<TMPDIR>
替换为实际的绝对路径(如
--body-file /var/folders/.../issue-flow-AbCdEf/body.md
)。不要
TMPDIR=
作为环境变量前缀添加到命令中——这会改变命令格式并触发权限提示。
不要使用
--body "$(cat <<'EOF'...)"
格式——
$()
命令替换会导致命令无法匹配任何预审批规则,因此管控shell命令的宿主会每次重新提示。
打印步骤1报告(见
references/report-format.md
)。

Sub-Issues (Optional)

子Issue(可选)

If the user mentioned other issue numbers to include in this aggregated issue, add them as sub-issues immediately after the parent is created — before printing the Step 1 report. See ## Sub-Issues for the procedure.
若用户提及要包含在汇总issue中的其他issue编号,在父issue创建完成后立即添加为子issue——在打印步骤1报告前执行。操作步骤见**## 子Issue**。

Sub-Issues

子Issue

Use when an aggregated (parent) issue should group related child issues. Needs the integer
.id
, not the issue number — they are different things. See
references/github-relationships.md
for ID type details.
用于汇总(父)issue分组相关子issue的场景。需要整数
.id
,而非issue编号——两者不同。ID类型详情见
references/github-relationships.md
。使用
repo-context.sh
输出中的
<owner>/<repo>
值——即
=== Repository ===
标题后的行,而非输出第一行。

Procedure

操作步骤

Fetch each child's integer ID, then POST it. Use the
<owner>/<repo>
value from
repo-context.sh
— it is the line after the
=== Repository ===
header, not the first line of output.
bash
PARENT=<parent_number>
for num in <child1> <child2> <child3>; do
  id=$(gh api repos/<owner>/<repo>/issues/$num --jq '.id')
  gh api repos/<owner>/<repo>/issues/$PARENT/sub_issues \
    --method POST \
    -F sub_issue_id="$id"
done
-F
(form field) is required —
-f
sends a string, causing
422
. The POST returns the parent issue object — parent title in response means success. Verify:
bash
gh api repos/<owner>/<repo>/issues/<parent_number>/sub_issues --jq '.[].number'
When a newly created issue is being linked as a child of an existing parent, also follow ## Project Inheritance From Parent so the child lands on the same project board(s) as the parent.
获取每个子issue的整数ID,然后执行POST请求。
bash
PARENT=<parent_number>
for num in <child1> <child2> <child3>; do
  id=$(gh api repos/<owner>/<repo>/issues/$num --jq '.id')
  gh api repos/<owner>/<repo>/issues/$PARENT/sub_issues \
    --method POST \
    -F sub_issue_id="$id"
done
必须使用
-F
(表单字段)——
-f
会发送字符串,导致
422
错误。POST请求返回父issue对象——响应中包含父标题表示成功。验证:
bash
gh api repos/<owner>/<repo>/issues/<parent_number>/sub_issues --jq '.[].number'
当新创建的issue被链接为现有父issue的子issue时,还需遵循**## 从父Issue继承项目**规则,使子issue与父issue处于相同的项目看板。

Project Inheritance From Parent

从父Issue继承项目

When a new issue is being created as a child of an existing parent (sub-issue link, "attach to #N", "linked to #N"), inherit the parent's project membership instead of using the generic Project picker.
Fetch the parent's projects (output:
<id>\t<title>
):
bash
bash "<skill-dir>/scripts/get-issue-projects.sh" <parent_number>
Apply based on count:
  • 0 — fall through to the generic Project subsection.
  • 1 — add automatically and tell the user which project was inherited.
  • 2+
    AskUserQuestion
    :
    1. "Add to all <N> parent projects" (Recommended)
    2. "Pick individually" — follow-up yes/no per project
    3. "Skip projects"
Run
add-to-project.sh <child_number> "<title>"
sequentially per selected project (parallel calls hit Projects V2 rate limits).
When linking many children to the same parent (see ## Batch Issue Creation), fetch the parent's projects once and reuse the decision for every child — do not prompt per child.
当新创建的issue作为现有父issue的子issue(子issue链接、"附加到#N"、"关联到#N")时,继承父issue的项目成员身份,而非使用通用的项目选择器。
获取父issue的项目(输出格式:
<id>\t<title>
):
bash
bash "<skill-dir>/scripts/get-issue-projects.sh" <parent_number>
根据数量执行:
  • 0个——执行通用项目选择流程。
  • 1个——自动添加并告知用户继承的项目。
  • 2个及以上——
    AskUserQuestion
    选项:
    1. "添加到所有<N>个父项目"(推荐)
    2. "单独选择"——逐个确认是否添加到对应项目
    3. "跳过项目"
按顺序为每个选中的项目运行
add-to-project.sh <child_number> "<title>"
(并行调用会触发Projects V2速率限制)。
当多个子issue链接到同一个父issue时(见**## 批量创建Issue**),仅获取一次父issue的项目,并将决策复用给所有子issue——不要逐个提示。

Blocked-By

依赖关系(Blocked-By)

Use when child issues have dependencies between them — e.g., issue B cannot start until issue A is done. Requires GraphQL node IDs (not issue numbers or integer IDs) — see
references/github-relationships.md
. Use the
<owner>/<repo>
value from
repo-context.sh
— it is the line after the
=== Repository ===
header, not the first line of output.
用于子issue之间存在依赖关系的场景——例如,issue B需在issue A完成后才能开始。需要GraphQL node ID(而非issue编号或整数ID)——详情见
references/github-relationships.md
。使用
repo-context.sh
输出中的
<owner>/<repo>
值——即
=== Repository ===
标题后的行,而非输出第一行。

Procedure

操作步骤

Step 1 — Fetch node IDs in one batch query:
bash
gh api graphql -f query='{
  repository(owner: "<owner>", name: "<repo>") {
    a: issue(number: <blocking-num>) { id }
    b: issue(number: <blocked-num>) { id }
  }
}'
Step 2 — Add relationship ("b is blocked by a"):
bash
gh api graphql -f query='mutation {
  addBlockedBy(input: {
    issueId: "<node-id-of-b>",
    blockingIssueId: "<node-id-of-a>"
  }) { issue { number } blockingIssue { number } }
}'
Use
removeBlockedBy
with the same signature to undo. See
references/github-relationships.md
for full details and ID type reference.
步骤1——批量查询获取node ID
bash
gh api graphql -f query='{
  repository(owner: "<owner>", name: "<repo>") {
    a: issue(number: <blocking-num>) { id }
    b: issue(number: <blocked-num>) { id }
  }
}'
步骤2——添加关系("b被a阻塞"):
bash
gh api graphql -f query='mutation {
  addBlockedBy(input: {
    issueId: "<node-id-of-b>",
    blockingIssueId: "<node-id-of-a>"
  }) { issue { number } blockingIssue { number } }
}'
使用
removeBlockedBy
并传递相同参数取消依赖关系。完整详情和ID类型参考见
references/github-relationships.md

Batch Issue Creation

批量创建Issue

When the user asks to create multiple issues at once (e.g., an epic with child issues, or a set of related issues):
当用户要求一次性创建多个issue时(如包含子issue的epic或一组相关issue):

Workflow

工作流程

  1. Resolve
    <TMPDIR>
    once via
    mktemp -d
  2. Create the parent/epic issue first (if applicable)
  3. Write all child issue body files with unique slugs:
    <TMPDIR>/<slug>-body.md
  4. Create all child issues in parallel (
    gh issue create
    calls) — apply ### Assignment Defaults once and reuse the same assignee for every issue in the batch, including the parent. Do not prompt per issue.
  5. Batch-link sub-issues to parent (if applicable) — use the for loop from ## Sub-Issues
  6. Add all issues to project sequentially — run
    add-to-project.sh
    in a for loop, one at a time (parallel calls cause API rate-limit failures and require retries). When children are linked to an existing parent, resolve the project set via ## Project Inheritance From Parent instead of asking generically.
  7. Ask about initial project status (e.g., "Backlog", "Current Sprint") via
    AskUserQuestion
    — then batch-update via
    project-status.sh
  8. Print a summary table at the end instead of per-issue Step 1 reports
  1. 通过
    mktemp -d
    解析一次
    <TMPDIR>
  2. 先创建父/epic issue(若有)
  3. 使用唯一的slug为所有子issue写入正文文件:
    <TMPDIR>/<slug>-body.md
  4. 并行创建所有子issue(
    gh issue create
    调用)——应用**### 默认分配规则**一次,并将相同的经办人复用给批量中的所有issue,包括父issue。不要逐个提示。
  5. 批量将子issue链接到父issue(若有)——使用**## 子Issue**中的for循环
  6. 按顺序将所有issue添加到项目——在for循环中运行
    add-to-project.sh
    ,逐个执行(并行调用会导致API速率限制失败并需要重试)。若子issue链接到现有父issue,通过**## 从父Issue继承项目**解析项目集,而非通用询问。
  7. 通过
    AskUserQuestion
    询问初始项目状态(如"待办"、"当前迭代")——然后通过
    project-status.sh
    批量更新
  8. 最后打印汇总表格,而非逐个issue的步骤1报告

Summary Table Format

汇总表格格式

| Issue | Title                   | Milestone   |
| ----- | ----------------------- | ----------- |
| [#10023](https://github.com/owner/repo/issues/10023) | AnchorDialog for Editor | Alpha (MVP) |
| [#10024](https://github.com/owner/repo/issues/10024) | Toolbar refactor        | Alpha (MVP) |
Omit the Milestone column if no milestone was assigned to any issue.
| Issue | 标题                   | 里程碑   |
| ----- | ----------------------- | ----------- |
| [#10023](https://github.com/owner/repo/issues/10023) | 编辑器的AnchorDialog | Alpha (MVP) |
| [#10024](https://github.com/owner/repo/issues/10024) | 工具栏重构        | Alpha (MVP) |
若没有issue分配里程碑,则省略里程碑列。

Step 2: Create Branch

步骤2:创建分支

Run
detect-base.sh
to find the correct base branch.
If the working tree is dirty (full flow from uncommitted changes):
bash
git checkout -b issue-<number>
Changes carry over to the new branch automatically. Skip checkout and pull — pulling on a dirty tree fails, and you're already on the base.
If the working tree is clean (entered at Step 2 directly, e.g. "start work on #N"):
bash
git checkout <base> && git pull origin <base>
git checkout -b issue-<number>
If
issue-<number>
already exists, use
AskUserQuestion
:
  1. "Switch to existing branch" (Recommended)
  2. "Delete and create fresh"
Update project status to "In Progress" (if project integration is available):
bash
bash "<skill-dir>/scripts/project-status.sh" <number> "In Progress"
Resolve the fork point for the report, so callers never have to turn the base name back into a rev:
bash
baseref=$(git rev-parse --verify --quiet "origin/$base" || git rev-parse --verify --quiet "$base")
git merge-base "$baseref" HEAD
Print the Step 2 report, including both
Base:
and
Fork:
.
运行
detect-base.sh
查找正确的基础分支。
若工作区有未提交变更(从未提交变更开始完整流程):
bash
git checkout -b issue-<number>
变更会自动携带到新分支。跳过checkout和pull操作——在脏工作区执行pull会失败,且当前已处于基础分支。
若工作区干净(直接切入步骤2,如"开始处理#N"):
bash
git checkout <base> && git pull origin <base>
git checkout -b issue-<number>
issue-<number>
分支已存在,通过
AskUserQuestion
询问用户:
  1. "切换到现有分支"(推荐)
  2. "删除现有分支并重新创建"
更新项目状态为"进行中"(若支持项目集成):
bash
bash "<skill-dir>/scripts/project-status.sh" <number> "In Progress"
解析报告的分叉点,调用方无需将基础名称转换为版本号:
bash
baseref=$(git rev-parse --verify --quiet "origin/$base" || git rev-parse --verify --quiet "$base")
git merge-base "$baseref" HEAD
打印步骤2报告,包含
Base:
Fork:
字段。

Step 3: Commit

步骤3:提交

This step owns the commit subject format, the commit body, and the squash rules for the whole pipeline. Nothing else defines them — callers delegate here.
本步骤管控整个流程中的提交主题格式、提交正文和合并规则。其他部分均不定义这些规则——调用方需委托给本步骤。

Subject

主题

Use
<Issue Title> #<number>
as the commit subject. The issue title is already in conventional commit format from Step 1 or from the
issue-analyze
skill.
If there is no linked issue (e.g., entered at Step 3 directly), use the repo's CLAUDE.md commit format or fall back to
<type>: <description>
.
No
wip:
subject may survive into the final history.
使用
<Issue Title> #<number>
作为提交主题。issue标题已在步骤1或
issue-analyze
技能中使用规范提交格式。
若未关联issue(如直接切入步骤3),使用仓库CLAUDE.md中的提交格式或回退到
<type>: <description>
最终提交历史中不得包含
wip:
主题。

Body

正文

Invoke the
commit-summary
skill to generate the commit body. It weighs the change and either derives a body from the code — what the running code does that forced the change, which call paths reach it, what breaks on update, when it broke, what was deliberately left alone — or returns two to three lines for a mechanical or generated change.
If the host cannot invoke another skill, or
commit-summary
is not installed, do the same inline. Derive when the change alters behaviour, a contract, a public type, or a default, or fixes a defect; otherwise write two or three past-tense lines and stop. To derive, answer in blank-line-separated paragraphs wrapped at 80, skipping any question with no real answer: what the running code does that forces the change (read the implementation, not the diff again), which call paths reach it and which are ruled out, how it failed observably, what breaks for someone who updates and why it is still correct, when it broke (
git log -S '<removed expression>' -- <path>
,
git blame
), what the tests pin, and what was noticed and deliberately not fixed. A hash appears only if a command returned it in this session; no result means the paragraph is dropped, not softened.
When the caller supplies design rationale pulled out of source comments (the
code-cleanup
skill produces this), it answers the first question — fold it into that paragraph. It is not a block to append at the end of the body.
调用
commit-summary
技能生成提交正文。该技能会评估变更内容,要么从代码中推导正文——运行代码的哪些行为导致了变更、哪些调用路径会影响该变更、更新时会破坏什么、何时出现问题、哪些内容被故意保留——要么为机械或生成的变更返回2-3行内容。
若宿主无法调用其他技能,或未安装
commit-summary
,则自行推导。当变更改变行为、契约、公共类型或默认值,或修复缺陷时,进行推导;否则编写2-3行过去式描述后停止。推导时,用空行分隔段落,每行不超过80字符,跳过无实际答案的问题:运行代码的哪些行为导致了变更(阅读实现,而非再次查看diff)、哪些调用路径会影响该变更并排除哪些路径、变更如何导致可观察的失败、更新时会破坏什么以及为何仍正确、何时出现问题(
git log -S '<removed expression>' -- <path>
git blame
)、测试固定了哪些内容、哪些问题被发现并故意未修复。仅当会话中命令返回哈希值时才包含哈希值;若命令无结果,则删除对应段落,而非弱化描述。
当调用方提供从源代码注释中提取的设计原理(
code-cleanup
技能会生成此类内容),该原理可回答第一个问题——将其合并到对应段落中。不要将其作为块附加到正文末尾。

No attribution

无署名

This step runs the
git commit
, so it is the last place an attribution footer can be caught. Read the assembled message before committing and remove any of these, wherever they came from — a body another skill returned, text a caller passed in, a message being amended:
  • "Drafted with AI", "Generated with", or any line naming a model, an assistant, or a tool
  • a session, chat, or transcript link
  • a
    <sub>
    attribution line, a trailing
    ---
    rule, a badge, or a promotional line
  • a
    Co-Authored-By
    trailer crediting an assistant
Do not add one either, and do not copy one forward from the previous commit. A trailer already in the log is not licence to repeat it; mirroring the last commit's footer is how one reproduces itself indefinitely.
A trailer crediting a human is fine where the repo's convention asks for one. The only exception is an explicit request — the repo's instruction file, the user's configuration, or the user's prompt asking for attribution outright. Finding one in
git log
is not that.
本步骤执行
git commit
,因此是最后可以捕获署名页脚的环节。提交前读取组装好的消息并移除以下内容,无论来源——其他技能返回的正文、调用方传递的文本、正在修改的消息:
  • "Drafted with AI"、"Generated with"或任何提及模型、助手或工具的行
  • 会话、聊天或转录链接
  • <sub>
    署名行、末尾的
    ---
    规则、徽章或推广行
  • 署名助手的
    Co-Authored-By
    trailer
也不要添加这些内容,且不要从之前的提交中复制。日志中已存在的trailer并不允许重复使用;镜像上一次提交的页脚会导致该内容无限重复。
若仓库规范要求,署名人类的trailer是可以的。唯一例外是明确要求——仓库的说明文件、用户配置或用户提示明确要求署名。在
git log
中找到署名并不属于这种情况。

Consolidate

合并提交

The branch must end this step at exactly one commit, and this section owns the only gate that can decide otherwise.
分支在本步骤结束时必须恰好有一个提交,本部分是唯一可以决定是否例外的环节。

Resolve the fork point

解析分叉点

Count and reset must use the same ref, and it must be an ancestor of
HEAD
. Resolve it once:
bash
base=$(bash "<skill-dir>/scripts/detect-base.sh" 2>/dev/null | tail -n1) || base=""
[ -n "$base" ] || { echo "No base branch — add a remote first."; exit 1; }
baseref=$(git rev-parse --verify --quiet "origin/$base" || git rev-parse --verify --quiet "$base")
fork=$(git merge-base "$baseref" HEAD)
git log "$fork"..HEAD --oneline
Stop if
base
is empty.
detect-base.sh
exits 2 with no output when the repo has no remote, and
git merge-base "" HEAD
just errors — there is no base to consolidate against.
Never reset to
origin/<base>
, and never to a branch name.
origin/<base>
moves whenever anything fetches, and
detect-base.sh
fetches on its own. Reset to it and the new commit's parent is newer than the point the branch was cut from, so the commit silently reverts every upstream change made since — and Step 5 force-pushes that, and Step 6 merges it.
$fork
is an ancestor of
HEAD
by construction, so it cannot have that effect.
计数和重置必须使用相同的引用,且该引用必须是
HEAD
的祖先。仅解析一次:
bash
base=$(bash "<skill-dir>/scripts/detect-base.sh" 2>/dev/null | tail -n1) || base=""
[ -n "$base" ] || { echo "No base branch — add a remote first."; exit 1; }
baseref=$(git rev-parse --verify --quiet "origin/$base" || git rev-parse --verify --quiet "$base")
fork=$(git merge-base "$baseref" HEAD)
git log "$fork"..HEAD --oneline
base
为空,停止操作。当仓库没有远程仓库时,
detect-base.sh
会以状态码2退出且无输出,
git merge-base "" HEAD
会直接报错——没有可合并的基础分支。
切勿重置到
origin/<base>
或分支名称
origin/<base>
会在每次fetch时更新,且
detect-base.sh
会自行执行fetch操作。重置到该引用会导致新提交的父节点比分支创建时的节点更新,因此提交会静默回滚自分支创建以来的所有上游变更——步骤5会强制推送该提交,步骤6会合并该提交。
$fork
HEAD
的祖先,因此不会出现这种情况。

Choose the action

选择操作

Commits ahead of
$fork
Action
0Nothing to unwind — go to Execute.
1, subject already canonicalKeep the commit. If the tree is dirty (a caller's comment trim or cruft deletion usually leaves it that way), stage the remaining edits and rewrite the message from Subject and Body:
git commit --amend -m "<subject>" -m "<body>"
. Do not use
--amend --no-edit
— it keeps the old message and discards the body this step just generated, including any rationale the caller passed in.
1, a
wip:
snapshot
git reset --soft "$fork"
, then Execute.
2+, one shared subject, or any
wip:
among them
git reset --soft "$fork"
, then Execute — one commit with the combined changes.
2+, genuinely different subjectsAsk before rewriting deliberate history: option 1
Squash into one commit
(Recommended)
, option 2
Keep as-is
. Squash →
git reset --soft "$fork"
, then Execute. Keep → skip Execute and report the commits as they stand; this is the one outcome that leaves more than one commit.
A caller that asked for a single commit (
"commit #<N>"
from an orchestrating skill) has already answered that question — squash without prompting.
超前于
$fork
的提交数
操作
0无需回退——进入执行环节。
1,主题已符合规范保留该提交。若工作区有未提交变更(通常是调用方的注释修剪或清理遗留的),暂存剩余编辑内容并根据主题正文重写消息:
git commit --amend -m "<subject>" -m "<body>"
不要使用
--amend --no-edit
——这会保留旧消息并丢弃本步骤生成的正文,包括调用方传递的原理。
1
wip:
快照
git reset --soft "$fork"
,然后进入执行环节。
2+,主题相同,或包含
wip:
快照
git reset --soft "$fork"
,然后进入执行环节——将所有变更合并为一个提交。
2+,主题确实不同在重写有意保留的历史前询问用户:选项1
合并为一个提交
(推荐)
,选项2
保持原样
。合并→
git reset --soft "$fork"
,然后进入执行环节。保持原样→跳过执行环节并按当前状态报告提交;这是唯一允许保留多个提交的情况。
若调用方要求单个提交(编排技能的
"commit #<N>"
指令),则已确认合并——无需提示直接合并。

Check the index before committing

提交前检查索引

git reset --soft
leaves the entire difference between
$fork
and your tree staged
— every file from every commit it unwound, including anything a
wip:
snapshot swept in. Naming files in Execute adds to that index; it does not narrow it. So after any reset, read the index and remove what must not ship:
bash
git diff --cached --name-only
git restore --staged <path>      # per file that does not belong in the commit
Deleting the file from disk is not enough once it is staged — unstage it. Untracked files are the one thing the reset does not capture; they stay untracked unless something adds them.
git reset --soft
会将**
$fork
与当前工作区的所有差异暂存**——包括回滚的每个提交中的所有文件,以及
wip:
快照包含的内容。执行环节中指定文件会添加到索引,而非缩小范围。因此,重置后需读取索引并移除不应提交的内容:
bash
git diff --cached --name-only
git restore --staged <path>      # 逐个移除不应提交的文件
文件已暂存后,仅删除磁盘上的文件是不够的——需取消暂存。未跟踪文件是重置不会捕获的内容;除非主动添加,否则会保持未跟踪状态。

Execute

执行

Stage what belongs in the commit, then commit:
bash
git add <files>
git commit -m "<subject>" -m "<body>"
Two different states reach this point, and the staging rule differs:
  • No reset happened (the
    0
    row). The index starts empty, so
    git add <files>
    fully determines the commit. Prefer naming files over
    git add -A
    — here it genuinely is the check that keeps scratch files out.
  • A reset happened. The index already holds everything Consolidate unwound. Naming files cannot narrow it, so Consolidate's index check is what keeps scratch out;
    git add
    here is only for files that were never committed.
Finish with
git status --short
showing nothing you meant to commit. Untracked files you deliberately left out may still be listed — that is expected, and Step 3 has no authority to delete them.
Print the Step 3 report.
暂存应提交的内容,然后提交:
bash
git add <files>
git commit -m "<subject>" -m "<body>"
有两种状态会进入该环节,暂存规则不同:
  • 未执行重置
    0
    行)。索引初始为空,因此
    git add <files>
    完全决定提交内容。优先指定文件而非
    git add -A
    ——这是防止临时文件被提交的检查手段。
  • 已执行重置。索引已包含合并提交回滚的所有内容。指定文件无法缩小范围,因此合并提交的索引检查是防止临时文件被提交的手段;
    git add
    仅用于添加从未提交过的文件。
结束时
git status --short
应显示无待提交内容。故意排除的未跟踪文件仍可能被列出——这是预期情况,步骤3无权删除这些文件。
打印步骤3报告。

Snapshot Mode

快照模式

Entered on intent "snapshot" — a caller needs the working tree frozen into a commit without finishing the work. Used to give reviewers a stable diff, or to checkpoint during a long implementation.
bash
git status --short          # confirm nothing scratch is about to be staged
git add -A && git commit -m "wip: snapshot"
Snapshot mode skips the Subject, Body, and Consolidate sections entirely:
  • The subject is content-free. A snapshot message describing the work leaks the implementer's reasoning into a place reviewers can read.
  • No
    commit-summary
    call — there is no body.
  • No report. Print one line:
    Snapshot: <short-sha>
    .
Snapshots are not final commits. A later Step 3 run squashes them away via Consolidate.
当用户意图为"snapshot"时进入该模式——调用方需要将工作区冻结为提交,但未完成工作。用于为评审者提供稳定的diff,或在长时间实现过程中创建检查点。
bash
git status --short          # 确认没有临时文件会被暂存
git add -A && git commit -m "wip: snapshot"
快照模式完全跳过主题、正文和合并提交环节:
  • 主题无实际内容。描述工作的快照消息会将实现者的推理泄露给评审者。
  • 不调用
    commit-summary
    ——无正文。
  • 不生成报告。仅打印一行:
    Snapshot: <short-sha>
快照不是最终提交。后续步骤3运行时会通过合并提交环节将其合并。

Step 4: Push

步骤4:推送

Push the branch to remote:
bash
git push -u origin issue-<number>
将分支推送到远程仓库:
bash
git push -u origin issue-<number>

Rebase if needed

必要时变基

If push fails because the remote has diverged, or if the user asks to rebase — capture the remote tip first, per Leasing a force-push:
bash
before=$(git rev-parse "origin/issue-<number>" 2>/dev/null || true)
git fetch origin <base>
git rebase origin/<base>
if [ -n "$before" ]; then
  git push --force-with-lease="issue-<number>:$before"
else
  git push -u origin issue-<number>
fi
若推送因远程仓库分支已偏离而失败,或用户要求变基——捕获远程分支的最新提交,遵循强制推送租赁
bash
before=$(git rev-parse "origin/issue-<number>" 2>/dev/null || true)
git fetch origin <base>
git rebase origin/<base>
if [ -n "$before" ]; then
  git push --force-with-lease="issue-<number>:$before"
else
  git push -u origin issue-<number>
fi

Amend

修改提交

If the user asks to amend the last commit:
bash
before=$(git rev-parse "origin/issue-<number>" 2>/dev/null || true)
git commit --amend -m "<subject>" -m "<body>"
if [ -n "$before" ]; then
  git push --force-with-lease="issue-<number>:$before"
else
  git push -u origin issue-<number>
fi
Always pass the message. A bare
git commit --amend
opens
$EDITOR
, which hangs a non-interactive shell. Use
--no-edit
only when the existing message is being kept verbatim and nothing new needs to land in it.
若用户要求修改最后一次提交:
bash
before=$(git rev-parse "origin/issue-<number>" 2>/dev/null || true)
git commit --amend -m "<subject>" -m "<body>"
if [ -n "$before" ]; then
  git push --force-with-lease="issue-<number>:$before"
else
  git push -u origin issue-<number>
fi
始终传递消息。仅
git commit --amend
会打开
$EDITOR
,导致非交互式shell挂起。仅当完全保留现有消息且无需添加新内容时,使用
--no-edit

Leasing a force-push

强制推送租赁

Every force-push in this pipeline pins the SHA it expects the remote to be at, captured before anything fetches:
bash
before=$(git rev-parse "origin/issue-<number>" 2>/dev/null || true)
本流程中的每次强制推送都会固定预期远程仓库的SHA,任何fetch操作捕获:
bash
before=$(git rev-parse "origin/issue-<number>" 2>/dev/null || true)

... rebase, amend, or consolidate ...

... 变基、修改或合并提交 ...

git push --force-with-lease="issue-<number>:$before"

`rev-parse` is guarded because the branch may not be on the remote yet, and a bare
`git rev-parse origin/issue-<number>` exits 128 with `unknown revision` — which under
`set -e` aborts the step instead of pushing. An empty `$before` means there is nothing to
lease against, so that case is a plain `git push -u origin issue-<number>`.

A bare `git push --force-with-lease` leases against `refs/remotes/origin/issue-<number>`.
Any fetch that refreshes that ref makes the lease describe where the remote is *now*
rather than where it was when you started — at which point it permits exactly the
overwrite it exists to prevent. `detect-base.sh` runs a bare `git fetch origin`, which
refreshes every branch, inside Step 3 → Consolidate; and nothing stops an unrelated fetch
landing between the rewrite and the push. Reading the SHA before any of that is what makes
the lease mean anything.

`--force-if-includes` (git 2.30+) is not a substitute here. `git help push` is explicit
that when it is combined with `--force-with-lease=<refname>:<expect>` it is a **no-op** —
so alongside the pinned form above it does literally nothing. It is the right tool for the
*valueless* `--force-with-lease`, since it consults the local reflog rather than the
remote-tracking ref; but it needs git 2.30+, it fails oddly after a `gc` or in a fresh
clone where the reflog is thin, and it would diverge from the pinned idiom used
everywhere else in this file. Pin the SHA instead.

If the push is rejected, the remote moved: fetch, rebase onto the new tip, and re-run
rather than escalating to `--force`.

Print the Step 4 report.
git push --force-with-lease="issue-<number>:$before"

`rev-parse`添加了错误处理,因为分支可能尚未推送到远程仓库,且`git rev-parse origin/issue-<number>`会因`unknown revision`以状态码128退出——若启用`set -e`会中止步骤而非推送。空`$before`表示无租赁对象,因此执行普通的`git push -u origin issue-<number>`。

仅`git push --force-with-lease`会租赁`refs/remotes/origin/issue-<number>`。任何刷新该引用的fetch操作都会使租赁描述远程仓库**当前**状态,而非开始操作时的状态——此时租赁会允许其本应阻止的覆盖操作。`detect-base.sh`在步骤3→合并提交环节中会执行`git fetch origin`,刷新所有分支;且在重写和推送之间可能会有无关的fetch操作。在这些操作前读取SHA才能使租赁有效。

`--force-if-includes`(git 2.30+)不能替代该方式。`git help push`明确说明,当与`--force-with-lease=<refname>:<expect>`结合使用时,该参数是**无效操作**——因此与上述固定格式结合使用时毫无作用。它是无值`--force-with-lease`的合适工具,因为它会查询本地reflog而非远程跟踪引用;但它需要git 2.30+,在`gc`后或reflog稀疏的新克隆中会异常失败,且与本文档中使用的固定方式不一致。因此固定SHA更可靠。

若推送被拒绝,说明远程仓库已更新:fetch远程仓库,变基到新的最新提交,然后重新运行,而非升级为`--force`。

打印步骤4报告。

Step 5: Create PR

步骤5:创建PR

Run
detect-base.sh
to determine the PR base.
运行
detect-base.sh
确定PR的基础分支。

Pre-PR: Squash Commits

创建PR前:合并提交

Apply Step 3 → Consolidate as-is. It owns the fork point, the count, the squash rules, and the one gate that may leave several commits — including the case where the single commit on the branch is a
wip:
snapshot, which must not reach a PR title. Do not restate any of those rules here; a second copy is how the two drift.
The only thing this step adds is that the branch is already on the remote, so the rewrite has to be force-pushed. Capture the remote tip before consolidating and lease against it explicitly, per Leasing a force-push:
bash
before=$(git rev-parse "origin/issue-<number>")
直接应用步骤3→合并提交规则。该环节管控分叉点、提交计数、合并规则以及允许保留多个提交的唯一条件——包括分支上的单个提交是
wip:
快照的情况,该快照不能作为PR标题。不要在此处重述任何规则——重复定义会导致两者不一致。
本步骤仅添加分支已推送到远程仓库的处理,因此重写后需强制推送。合并提交前捕获远程分支的最新提交并明确租赁,遵循强制推送租赁
bash
before=$(git rev-parse "origin/issue-<number>")

... apply Step 3 -> Consolidate ...

... 应用步骤3→合并提交 ...

git push --force-with-lease="issue-<number>:$before"

This must happen before PR body generation, since consolidating changes the commit log.
git push --force-with-lease="issue-<number>:$before"

这必须在生成PR正文前执行,因为合并提交会改变提交日志。

Title and Body

标题和正文

  • Title:
    <Issue Title> #<number>
  • Body: Generate from
    git log "$fork"..HEAD --oneline
    — reuse the
    $fork
    that Step 3 → Consolidate just resolved. Do not write
    git log <base>..HEAD
    :
    <base>
    is a branch name, and an epic branch that exists only on the remote fails there with
    unknown revision
    . Add
    Closes #<number>
    .
markdown
undefined
  • 标题
    <Issue Title> #<number>
  • 正文:从
    git log "$fork"..HEAD --oneline
    生成——复用步骤3→合并提交刚解析的
    $fork
    。不要使用
    git log <base>..HEAD
    <base>
    是分支名称,仅存在于远程仓库的epic分支会因
    unknown revision
    失败。添加
    Closes #<number>
markdown
undefined

Changes

变更

  • <change 1>
  • <change 2>
Closes #<number>
undefined
  • <变更1>
  • <变更2>
Closes #<number>
undefined

Assignee and Reviewer

经办人和评审人

Follow ### Assignment Defaults. Reviewer selection is separate from assignment: the current user is an assignee on every PR this skill creates, whatever the reviewer outcome.
In a
KIND=personal
repo, skip the reviewer prompt entirely unless the repo's CLAUDE.md sets a reviewer rule or the user asked for one — a solo repo has no one else to review, and the reviewer question is the step where assignment silently gets dropped.
Check the target repo's CLAUDE.md for reviewer rules (e.g., "PRs to main should be reviewed by @username", default reviewer for specific branches). If a matching rule exists, use that reviewer directly.
Otherwise, run
pr-reviewers.sh
to find users with actual recent PR review activity:
bash
bash "<skill-dir>/scripts/pr-reviewers.sh"
Output:
<user>\t<count>
per row, up to 3 rows (excludes self and bots; counts both reviews submitted and review-requests received across the last 100 PRs in any state).
  • 0 results: skip the prompt entirely and create the PR without
    --reviewer
    . Do not fall back to the generic
    repo-context.sh
    collaborator list — repo members with zero review history are not real reviewer candidates, and inventing labels like "Frequent collaborator" misleads the user.
  • 1+ results: compose
    AskUserQuestion
    :
    1. First result (Recommended), description:
      "<count> review events across the last 100 PRs"
    2. Second result (if any), description:
      "<count> review events across the last 100 PRs"
    3. "No reviewer"
Check if the selected reviewer is the same as the PR creator:
bash
gh api user --jq .login
If same, skip
--reviewer
flag (GitHub doesn't allow self-review).
遵循**### 默认分配规则**。评审人选择与经办人选择分开:本技能创建的每个PR中,当前用户都是经办人,无论评审人选择结果如何。
KIND=personal
仓库中,除非仓库CLAUDE.md设置了评审人规则或用户要求,否则完全跳过评审人提示——单人仓库没有其他评审人,且评审人环节会导致经办人被静默丢弃。
检查目标仓库CLAUDE.md中的评审人规则(如"main分支的PR需由@username评审"、特定分支的默认评审人)。若存在匹配规则,直接使用该评审人。
否则,运行
pr-reviewers.sh
查找近期有实际PR评审活动的用户:
bash
bash "<skill-dir>/scripts/pr-reviewers.sh"
输出格式:每行
<user>\t<count>
,最多3行(排除自己和机器人;统计最近100个PR中提交的评审和收到的评审请求)。
  • 0个结果:完全跳过提示,创建PR时不使用
    --reviewer
    参数。不要回退到
    repo-context.sh
    获取的通用协作者列表——无评审历史的仓库成员不是真正的评审候选人,使用"频繁协作者"等标签会误导用户。
  • 1个及以上结果:构造
    AskUserQuestion
    选项:
    1. 第一个结果(推荐),描述:
      "最近100个PR中有<count>次评审活动"
    2. 第二个结果(若有),描述:
      "最近100个PR中有<count>次评审活动"
    3. "无评审人"
检查选中的评审人是否与PR创建者相同:
bash
gh api user --jq .login
若相同,跳过
--reviewer
参数(GitHub不允许自我评审)。

Assignees

经办人

The PR creator (
@me
, i.e. the current user) is always an assignee — in personal and org repos alike, with or without a reviewer. Setting a reviewer never replaces the creator on Assignees; it adds to it.
  • Reviewer set (and not the creator): assign
    @me
    and the reviewer.
    • e.g. creator
      alice
      sets
      octocat
      as reviewer → Reviewers:
      octocat
      , Assignees:
      octocat
      ,
      alice
      .
  • No reviewer (or reviewer is the creator / self-review): assign only
    @me
    .
There is no branch of this step that produces zero assignees. If a reviewer prompt is skipped or
pr-reviewers.sh
returns nothing,
@me
still goes on
--assignee
.
PR创建者(
@me
,即当前用户)始终是经办人——无论个人仓库还是组织仓库,无论是否有评审人。设置评审人不会替换创建者的经办人身份;只会添加到经办人列表。
  • 已设置评审人(且评审人不是创建者):分配
    @me
    评审人。
    • 例如,创建者
      alice
      设置
      octocat
      为评审人 → 评审人:
      octocat
      ,经办人:
      octocat
      alice
  • 无评审人(或评审人是创建者/自我评审):仅分配
    @me
本步骤不会出现无经办人的情况。若跳过评审人提示或
pr-reviewers.sh
无输出,
@me
仍会被添加到
--assignee
参数。

Create

创建

Write the PR body to
<TMPDIR>/pr-body.md
with the host's file-write tool, then create the PR with
--body-file
. Replace
<TMPDIR>
with the literal absolute path — do NOT use
TMPDIR=
as an env var prefix.
With a reviewer (pass
--assignee
once per assignee):
bash
gh pr create --title "<title>" --body-file <TMPDIR>/pr-body.md --base <base> --assignee @me --assignee <reviewer> --reviewer <reviewer>
Without a reviewer (or self-review):
bash
gh pr create --title "<title>" --body-file <TMPDIR>/pr-body.md --base <base> --assignee @me
Do NOT use
--body "$(cat <<'EOF'...)"
— the
$()
command substitution makes the command unmatchable against any pre-approval rule, so hosts that gate shell commands re-prompt every time.
Update project status to "Review":
bash
bash "<skill-dir>/scripts/project-status.sh" <number> "Review"
通过宿主的文件写入工具将PR正文写入
<TMPDIR>/pr-body.md
,然后使用
--body-file
参数创建PR。将
<TMPDIR>
替换为实际的绝对路径——不要使用
TMPDIR=
作为环境变量前缀。
指定评审人时(每个经办人传递一次
--assignee
):
bash
gh pr create --title "<title>" --body-file <TMPDIR>/pr-body.md --base <base> --assignee @me --assignee <reviewer> --reviewer <reviewer>
无评审人时(或自我评审):
bash
gh pr create --title "<title>" --body-file <TMPDIR>/pr-body.md --base <base> --assignee @me
不要使用
--body "$(cat <<'EOF'...)"
格式——
$()
命令替换会导致命令无法匹配任何预审批规则,因此管控shell命令的宿主会每次重新提示。
更新项目状态为"评审中":
bash
bash "<skill-dir>/scripts/project-status.sh" <number> "Review"

Verify mergeability

可合并性验证

Always run this after creating a PR, whether or not Step 6 will follow. A PR that cannot merge is not a finished step, and reporting "PR created" without checking hides that.
GitHub computes
mergeable
asynchronously, so it returns
UNKNOWN
for the first second or two — poll until it settles:
bash
for i in 1 2 3; do
  state=$(gh pr view <pr-number> --json mergeable,mergeStateStatus --jq '.mergeable + " " + .mergeStateStatus')
  case "$state" in UNKNOWN*) sleep 3 ;; *) break ;; esac
done
echo "$state"
ResultAction
MERGEABLE
Report
Mergeable: yes
on the Step 5 report and continue.
CONFLICTING
Rebase onto base (
git fetch origin && git rebase origin/<base>
), force-push, re-run the poll. If conflicts are not mechanical, invoke the
resolve-conflicts
skill; if still unresolved, report
Mergeable: no — conflicts with <base>
and stop before Step 6.
UNKNOWN
after 3 polls
Report
Mergeable: unknown (GitHub still computing)
. Do not treat as failure.
mergeStateStatus
adds context worth reporting when it is not
CLEAN
:
  • BEHIND
    — base moved ahead; rebase and force-push.
  • BLOCKED
    — branch protection or a required review is pending. Not a conflict; report it as-is.
  • UNSTABLE
    — checks are failing or still running.
Do not run
gh pr checks --watch
here. Check monitoring belongs to Step 6; a PR-only flow reports the mergeability state and ends.
Print the Step 5 report, including the
Mergeable:
line.
创建PR后始终执行该步骤,无论是否会进入步骤6。无法合并的PR不是已完成的步骤,仅报告"PR已创建"而不检查会隐藏该问题。
GitHub会异步计算
mergeable
状态,因此最初几秒钟会返回
UNKNOWN
——轮询直到状态稳定:
bash
for i in 1 2 3; do
  state=$(gh pr view <pr-number> --json mergeable,mergeStateStatus --jq '.mergeable + " " + .mergeStateStatus')
  case "$state" in UNKNOWN*) sleep 3 ;; *) break ;; esac
done
echo "$state"
结果操作
MERGEABLE
在步骤5报告中添加
Mergeable: yes
并继续。
CONFLICTING
变基到基础分支(
git fetch origin && git rebase origin/<base>
),强制推送,重新轮询。若冲突无法自动解决,调用
resolve-conflicts
技能;若仍无法解决,报告
Mergeable: no — conflicts with <base>
在步骤6前停止
3次轮询后仍为
UNKNOWN
报告
Mergeable: unknown (GitHub still computing)
。不要视为失败。
mergeStateStatus
不是
CLEAN
时,需添加上下文报告:
  • BEHIND
    ——基础分支已更新;变基并强制推送。
  • BLOCKED
    ——分支保护或必需的评审待处理。不是冲突;按实际状态报告。
  • UNSTABLE
    ——检查失败或仍在运行。
不要在此处运行
gh pr checks --watch
。检查监控属于步骤6;仅创建PR的流程会报告可合并性状态并结束。
打印步骤5报告,包含
Mergeable:
行。

Step 6: Merge PR

步骤6:合并PR

Skip Condition

跳过条件

Determine the current user:
gh api user --jq .login
. If the PR reviewer OR assignee is someone other than the current user, skip Step 6:
PR #<number> is ready for review by @<reviewer> (mergeable: <state from Step 5>). Merge skipped — awaiting external review.
If reviewer AND assignee are the current user (self-review), or user explicitly asked to merge, proceed below.
确定当前用户:
gh api user --jq .login
。若PR评审人经办人不是当前用户,跳过步骤6
PR #<number>已准备好由@<reviewer>评审(可合并性:<步骤5中的状态>)。已跳过合并——等待外部评审。
若评审人和经办人均为当前用户(自我评审),或用户明确要求合并,继续执行以下步骤。

Entering Step 6 at all

进入步骤6的条件

Step 6 runs only when merging was asked for. Reaching Step 5 is not an invitation to merge — a caller whose intent was
"push and open PR for #<N>"
wants the flow to end at Step 5, and suggesting a merge there overrides a choice the user already made.
Step 6 is entered on exactly three things: an intent naming the merge (
"merge #<N>"
,
"push, PR, and merge #<N>"
), the user answering the suggestion below, or the user asking to merge in conversation. Otherwise stop after Step 5.
仅当用户要求合并时才运行步骤6。进入步骤5并不意味着可以合并——若用户意图是
"推送并为#<N>打开PR"
,则流程应在步骤5结束,此时建议合并会覆盖用户已做出的选择。
仅在三种情况下进入步骤6:用户意图明确提及合并(如
"merge #<N>"
"push, PR, and merge #<N>"
)、用户同意以下建议、或用户在对话中要求合并。否则在步骤5结束。

Confirm exactly once

仅确认一次

The merge is confirmed once per flow, and this section decides where that happens. Classify the entry, then take one branch and skip the other.
EntryConfirmation
The intent already names the merge
"push, PR, and merge #<N>"
,
"merge #<N>"
from an orchestrating skill that already put the question to the user
Already confirmed. Print the pre-merge summary for the record and go straight to Pre-checks. Asking again is the duplicate prompt callers are told not to create.
A full flow that entered at Step 1 and was never told what to do about mergingNot yet confirmed. Print the pre-merge summary and suggest merging (below).
Direct entry at Step 6 by the user, no earlier step in this flowNot yet confirmed. Print the pre-merge summary and MUST stop and confirm before merging.
When confirmation is still needed, ask via
AskUserQuestion
:
  1. "Merge now" (Recommended) — wait for checks and merge
  2. "Skip" — leave PR open, end flow
"Skip" → print the skip message and stop. "Merge now" → continue to Pre-checks.
Either way the pre-merge summary is printed (see
references/report-format.md
) — it is the record of what is about to be merged, not the prompt itself.
The suggestion branch additionally requires all of:
  • Issue assignee is the current user
  • PR assignee is the current user
  • No external reviewer was set on the PR
If any of those is false, the Skip Condition above already applies and Step 6 ends.
每个流程仅确认一次合并,本部分决定确认时机。分类进入方式,然后执行对应分支并跳过其他分支。
进入方式确认方式
意图明确提及合并——
"push, PR, and merge #<N>"
、编排技能的
"merge #<N>"
(已向用户确认)
已确认。打印合并前汇总记录并直接进入预检查环节。再次询问会重复提示,调用方应避免这种情况。
从步骤1开始的完整流程,未告知合并相关操作未确认。打印合并前汇总并建议合并(如下)。
用户直接进入步骤6,流程中无更早步骤未确认。打印合并前汇总并必须停止并确认后再合并。
当仍需确认时,通过
AskUserQuestion
询问:
  1. "立即合并"(推荐)——等待检查通过后合并
  2. "跳过"——保持PR打开,结束流程
选择"跳过"→打印跳过消息并停止。选择"立即合并"→继续进入预检查环节。
无论哪种情况,都会打印合并前汇总(见
references/report-format.md
)——这是即将合并内容的记录,而非提示本身。
建议合并分支还需满足以下所有条件:
  • issue经办人是当前用户
  • PR经办人是当前用户
  • PR未设置外部评审人
若任一条件不满足,上述跳过条件已适用,步骤6结束。

Pre-checks

预检查

  1. Check PR state:
bash
gh pr view <pr-number> --json state,mergeable
  • If PR is not open: report current state and stop.
  • If there are conflicts: rebase onto base, force-push, then continue to step 2.
  1. Wait for CI checks using
    gh pr checks --watch
    (use a 5-minute Bash timeout):
bash
gh pr checks <pr-number> --watch --fail-fast
  • Exit 0 (all passed/skipped) → proceed to step 3
  • Exit 1 (failure) → report failed checks, stop. Do not merge.
  • Bash timeout → report timeout, ask user via
    AskUserQuestion
    :
    1. "Merge anyway" — proceed to step 3
    2. "Wait longer" — re-run
      gh pr checks --watch --fail-fast
      with another 5-minute timeout
    3. "Abort" — stop
  1. After checks pass, verify mergeability one more time:
bash
gh pr view <pr-number> --json mergeable --jq '.mergeable'
If conflicts appeared, rebase and re-run checks.
  1. 检查PR状态:
bash
gh pr view <pr-number> --json state,mergeable
  • 若PR未打开:报告当前状态并停止
  • 若存在冲突:变基到基础分支,强制推送,然后进入步骤2。
  1. 使用
    gh pr checks --watch
    等待CI检查完成(设置5分钟Bash超时):
bash
gh pr checks <pr-number> --watch --fail-fast
  • 状态码0(所有检查通过/跳过)→进入步骤3
  • 状态码1(检查失败)→报告失败的检查,停止。不要合并。
  • Bash超时→报告超时,通过
    AskUserQuestion
    询问用户:
    1. "仍合并"→进入步骤3
    2. "继续等待"→使用另一个5分钟超时重新运行
      gh pr checks --watch --fail-fast
    3. "中止"→停止
  1. 检查通过后,再次验证可合并性:
bash
gh pr view <pr-number> --json mergeable --jq '.mergeable'
若出现冲突,变基并重新运行检查。

Merge

合并

bash
gh pr merge --rebase --delete-branch
Closes #<number>
in the PR body auto-closes the issue when merging into the default branch. Only verify and manually close if the merge target is a non-default branch (e.g., epic-* or next):
bash
undefined
bash
gh pr merge --rebase --delete-branch
PR正文中的
Closes #<number>
会在合并到默认分支时自动关闭issue。仅当合并目标为非默认分支(如epic-*或next)时,才需手动验证并关闭issue:
bash
undefined

Only run this if base branch is NOT the default branch

仅当基础分支不是默认分支时运行

gh issue close <number>

Update project status to "Done":

```bash
bash "<skill-dir>/scripts/project-status.sh" <number> "Done"
Print the Step 6 merged report.
gh issue close <number>

更新项目状态为"已完成":

```bash
bash "<skill-dir>/scripts/project-status.sh" <number> "Done"
打印步骤6合并报告。

Error Handling

错误处理

  • Projects V2 fails: Warn once, then skip all project operations for the rest of the flow. The core lifecycle works without project integration.
  • repo-ownership.sh
    fails
    : Treat as
    personal
    — assign the current user and continue. Do not prompt, and do not leave the issue or PR unassigned.
  • Assignment rejected (
    gh
    reports the assignee is not a valid collaborator): report which assignee was dropped, then continue. Do not abort the flow or retry with a different user.
  • gh not authenticated: Stop immediately, tell user to run
    gh auth login
    .
  • Branch already exists: Ask user via
    AskUserQuestion
    (switch vs. recreate).
  • CI checks failing: Report failed checks, do not attempt merge.
  • No CLAUDE.md: Use the default conventions listed above.
  • No remote:
    detect-base.sh
    exits 2 and there is no base branch. Steps 2, 3, 5, and 6 all depend on it, so stop at whichever of them was entered and tell the user to add a remote. Do not fall back to
    main
    .
  • Host cannot show structured choices: Fall back to a numbered list in chat per ### Asking the User. Do not skip the question.
  • Projects V2失败:警告一次,然后流程中剩余的项目操作全部跳过。核心生命周期无需项目集成即可运行。
  • repo-ownership.sh
    失败
    :视为
    personal
    仓库——分配给当前用户并继续。不要提示,也不要让issue或PR无人分配。
  • 分配被拒绝
    gh
    报告经办人不是有效协作者):报告被移除的经办人,然后继续。不要中止流程或重试其他用户。
  • gh未认证:立即停止,告知用户运行
    gh auth login
  • 分支已存在:通过
    AskUserQuestion
    询问用户(切换到现有分支还是重新创建)。
  • CI检查失败:报告失败的检查,不要尝试合并。
  • 无CLAUDE.md:使用上述默认规范。
  • 无远程仓库
    detect-base.sh
    以状态码2退出且无基础分支。步骤2、3、5、6均依赖基础分支,因此在进入的步骤停止并告知用户添加远程仓库。不要回退到
    main
    分支。
  • 宿主无法显示结构化选择:回退到**### 询问用户**中的聊天编号列表形式。不要跳过问题。

Integration

集成

  • For commit message body → invoke the
    commit-summary
    skill; fall back to the inline derivation in Step 3 → Body if the host cannot chain skills
  • For project token setup → see
    references/project-integration.md
  • For report templates → see
    references/report-format.md
  • For sub-issues and blocked-by relationships → see
    references/github-relationships.md
  • 提交消息正文→调用
    commit-summary
    技能;若宿主无法链式调用技能,回退到步骤3→正文中的内联推导
  • 项目令牌设置→见
    references/project-integration.md
  • 报告模板→见
    references/report-format.md
  • 子issue和依赖关系→见
    references/github-relationships.md