create-issue-tree
create-issue-tree
要件・タスク一覧から Phase 分割された GitHub Issue ツリーを新規作成する。
ルート(トラッキング issue)→ Phase 親 issue → issue → sub-issue の 4 階層を構築し、implement-issue-tree が post-order DFS で消化できる構造を維持する。
从需求与任务列表中创建按Phase划分的GitHub Issue树。
构建根(追踪Issue)→ Phase父Issue→ Issue→ sub-issue的4层结构,维持可被implement-issue-tree通过后序DFS方式处理的结构。
引数としてタスク要件テキストまたはファイルパスを渡す。
オプションで特定 Phase のみ起票することもできる(大規模ツリーを段階的に起票する場合)。
2 回目以降の部分起票では
で既存ルート issue 番号を渡し、同じツリーに継ぎ足す
(指定しないと新しいルート issue が重複作成される)。
オプションで起票する issue 全件に割り当てる GitHub Milestone を指定できる
(
指定時は省略可。既存ルートの milestone を自動継承する)。
create-issue-tree "ユーザー認証機能を実装する"
create-issue-tree requirements.md
create-issue-tree requirements.md --phase 2 --root 123
create-issue-tree requirements.md --milestone "v2.0"
传入任务需求文本或文件路径作为参数。
可通过
选项仅发起指定Phase的Issue(适用于大规模树的分步发起场景)。
第二次及以后的部分发起需通过
传入已存在的根Issue编号,以追加到同一树中
(若不指定,会重复创建新的根Issue)。
可通过
选项为所有发起的Issue指定GitHub Milestone
(指定
时可省略,会自动继承已有根Issue的milestone)。
create-issue-tree "实现用户认证功能"
create-issue-tree requirements.md
create-issue-tree requirements.md --phase 2 --root 123
create-issue-tree requirements.md --milestone "v2.0"
- CLI がインストールされ、認証済みであること( で確認)
- 対象リポジトリへの Issue 書き込み権限があること
- 已安装 CLI并完成认证(可通过确认)
- 拥有目标仓库的Issue写入权限
Step 1: 要件を分析してタスクを分解する
Step 1: 分析需求并分解任务
入力テキストまたはファイルから要件を読み込み、以下の観点でタスクを分解する。
- 粒度基準: 1 issue は実装 4h 程度に収める。 4h を超えると判断した場合は sub-issue に再分解する
- 各タスクの依存関係・実行順を把握する
- タスク数を集計し、Phase 分割の要否を判断する(目安: 10 件超で Phase 分割を検討)
分解結果をユーザーに提示し、Phase 構成・issue 数について確認を取る。
オプションが指定されている場合は該当 Phase のタスクのみ対象とする。
从输入文本或文件中读取需求,从以下维度分解任务:
- 粒度标准:1个Issue的实现工作量控制在4小时左右。 若判断超过4小时,则进一步分解为sub-issue
- 掌握各任务的依赖关系与执行顺序
- 统计任务数量,判断是否需要进行Phase划分(参考标准:任务数超过10件时考虑Phase划分)
将分解结果展示给用户,确认Phase构成与Issue数量。
若指定了
选项,则仅处理对应Phase的任务。
Step 2: Phase 構成を設計する
Step 2: 设计Phase构成
タスク数・依存関係をもとに Phase を設計する。
- Phase は独立して開発可能な単位で分割する(例: Phase 1 基盤・Phase 2 機能・Phase 3 改善)
- 各 Phase に親 issue タイトル(Conventional Commits 形式推奨)を割り当てる
- 全体をまとめるルート(トラッキング)issue のタイトルを決定する
- 例:
chore(global): 全 open issue の Phase 別トラッキング (YYYY-MM-DD)
タスクが少数(Phase 分割不要)の場合はルート issue + 子 issue の 2 階層構成にする。
基于任务数量与依赖关系设计Phase:
- Phase需按可独立开发的单元划分(例如:Phase 1 基础架构、Phase 2 功能实现、Phase 3 优化改进)
- 为每个Phase分配父Issue标题(推荐使用Conventional Commits格式)
- 确定用于汇总全局的根(追踪)Issue标题
- 示例:
chore(global): 所有open Issue的Phase别追踪 (YYYY-MM-DD)
若任务数量较少(无需Phase划分),则采用根Issue + 子Issue的2层结构。
Step 2.5: milestone を決定する
Step 2.5: 确定milestone
このツリーに割り当てる GitHub Milestone を決定する。ルート・Phase 親・子・sub-issue の
全件に同一 milestone を適用する(ツリー単位で 1 milestone)。
が指定されている場合は、ここで先に
を設定し、milestone の
継承・書き込みを行う前に OPEN 状態を検証する(closed なルートの milestone を
書き換えてから中断する事故を防ぐ。Step 3 側では再代入・再検証しない)。
确定为该树分配的GitHub Milestone。根Issue、Phase父Issue、子Issue、sub-issue均应用同一milestone(树级统一1个milestone)。
若指定了
,需先在此处设置
,并在继承/写入milestone前验证根Issue处于OPEN状态(避免出现修改已关闭根Issue的milestone后中断操作的问题。Step 3不再重复赋值与验证)。
--root で渡された Issue 番号を実際の値で代入する(実行時に Claude が置き換える)
将--root传入的Issue编号替换为实际值(执行时由Claude替换)
例: --root 123 が指定された場合 → ROOT_NUMBER="123" / --root 未指定の場合 → ROOT_NUMBER=""
示例:指定--root 123时 → ROOT_NUMBER="123" / 未指定--root时 → ROOT_NUMBER=""
ROOT_NUMBER="<--root で渡された Issue 番号(未指定なら空文字)>"
ROOT_NUMBER="<--root传入的Issue编号(未指定则为空字符串)>"
--root 指定時のみ: OPEN でなければ milestone 操作より前に中止する
仅当指定--root时:若根Issue非OPEN状态,在milestone操作前终止
(新規ツリー作成で ROOT_NUMBER が空の場合はこの検証をスキップする)
(新建树且ROOT_NUMBER为空时跳过此验证)
if [[ -n "${ROOT_NUMBER}" ]]; then
ROOT_STATE=$(gh issue view "${ROOT_NUMBER}" --json state --jq '.state')
if [[ "${ROOT_STATE}" != "OPEN" ]]; then
echo "エラー: ルート issue #${ROOT_NUMBER} は OPEN ではありません (state: ${ROOT_STATE})。中止します。"
exit 1
fi
fi
**milestone 非運用リポジトリのガード**: `--milestone` が明示されていない場合、リポジトリに
milestone が 1 件も存在しなければ(closed 含む)milestone 非運用リポジトリとみなし、
以降の確認をすべてスキップして `MILESTONE` は空のまま Step 3 へ進む
(milestone を使わないリポジトリの起票フローに確認を増やさない)。
```bash
MILESTONE_COUNT=$(gh api "repos/{owner}/{repo}/milestones?state=all" --jq 'length')
if [[ -n "${ROOT_NUMBER}" ]]; then
ROOT_STATE=$(gh issue view "${ROOT_NUMBER}" --json state --jq '.state')
if [[ "${ROOT_STATE}" != "OPEN" ]]; then
echo "错误:根Issue #${ROOT_NUMBER} 不是OPEN状态(状态:${ROOT_STATE})。终止操作。"
exit 1
fi
fi
**非milestone运营仓库防护**: 若未指定`--milestone`,且仓库中不存在任何milestone(含已关闭),则视为非milestone运营仓库,跳过后续所有确认步骤,保持`MILESTONE`为空进入Step 3
(避免给不使用milestone的仓库增加额外确认步骤)。
```bash
MILESTONE_COUNT=$(gh api "repos/{owner}/{repo}/milestones?state=all" --jq 'length')
MILESTONE_COUNT が 0 かつ --milestone 未指定なら、このステップの残りをスキップする
若MILESTONE_COUNT为0且未指定--milestone,则跳过此步骤剩余操作
優先順位は **`--milestone` > `--root` からの継承 > ユーザーへの確認** の順。
- `--milestone` が指定されている場合: その値をそのまま `MILESTONE` として使用する
(`--root` も同時指定されている場合、ルート側の milestone より `--milestone` を優先する。
ルート側と異なる値の場合は、ルートの milestone も合わせて更新してよいかユーザーに確認する。
更新しないと回答された場合はツリー内で milestone が混在する点を伝えたうえで続行する)
- `--milestone` 未指定かつ `--root` 指定時: 既存ルートの milestone を自動継承する
(milestone が取得できた場合のみユーザーへの確認は不要)
```bash
MILESTONE=$(gh issue view "${ROOT_NUMBER}" --json milestone --jq '.milestone.title // empty')
が空(既存ルートに milestone が未設定)の場合は自動継承とみなさず、
「どちらも未指定の場合」と同じユーザー確認フローへ進む(リポジトリに milestone が
存在するのにサイレントに milestone なしで進行しない。milestone が 1 件もない場合は
冒頭の非運用ガードが先に働くため、この確認には到達しない)。
-
どちらも未指定の場合、または
指定時に継承すべき milestone が空だった場合:
milestone を割り当てるかユーザーに確認する。
割り当てる場合はオープン中の milestone 一覧を提示して選ばせるか、新規 milestone 名の
入力を受け付けて
に設定する。割り当てないと回答されたら
は
空のまま Step 3 以降へ進む(issue は milestone なしで作成される)。
bash
gh api "repos/{owner}/{repo}/milestones" --jq '.[] | select(.state=="open") | .title'
ユーザーが一覧にない新規 milestone 名を入力した場合、
gh issue create --milestone
は
既存の milestone 名しか受け付けないため、使用前に milestone 自体を作成する。
同名の closed milestone が既に存在すると作成が 422(already_exists)で失敗するため、
その場合は reopen するか別名にするかをユーザーに確認する。
bash
gh api --method POST "repos/{owner}/{repo}/milestones" -f "title=${MILESTONE}"
指定時に継承すべき milestone が空でこのフローに合流した場合、決定した
を既存ルート issue にも反映する(子だけ milestone が付き、ルートが
未設定のまま残る不整合を防ぐ)。
bash
if [[ -n "${ROOT_NUMBER}" && -n "${MILESTONE}" ]]; then
gh issue edit "${ROOT_NUMBER}" --milestone "${MILESTONE}"
fi
优先级顺序为 **`--milestone` > 从`--root`继承 > 用户确认**:
- 若指定了`--milestone`:直接使用该值作为`MILESTONE`
(若同时指定了`--root`,则`--milestone`优先级高于根Issue的milestone。若二者值不同,需询问用户是否同步更新根Issue的milestone。若用户选择不更新,则需告知用户树内milestone会存在不一致后再继续)
- 未指定`--milestone`但指定了`--root`:自动继承已有根Issue的milestone
(成功获取到milestone时无需用户确认)
```bash
MILESTONE=$(gh issue view "${ROOT_NUMBER}" --json milestone --jq '.milestone.title // empty')
若
为空(已有根Issue未设置milestone),则不视为自动继承,进入「二者均未指定」的用户确认流程(避免仓库存在milestone却静默无milestone推进。若仓库无任何milestone,开头的非运营防护会先触发,不会进入此确认环节)。
-
二者均未指定,或指定
时无可用继承milestone:
询问用户是否分配milestone。若分配,则展示当前开放的milestone列表供用户选择,或接收用户输入的新milestone名称并设置为
。若用户选择不分配,则保持
为空进入Step 3以后的流程(Issue将无milestone创建)。
bash
gh api "repos/{owner}/{repo}/milestones" --jq '.[] | select(.state=="open") | .title'
若用户输入列表中不存在的新milestone名称,由于
gh issue create --milestone
仅接受已存在的milestone名称,需先创建milestone本身。
若已存在同名的已关闭milestone,创建会因422(already_exists)失败,此时需询问用户是重新打开该milestone还是使用其他名称。
bash
gh api --method POST "repos/{owner}/{repo}/milestones" -f "title=${MILESTONE}"
若指定
且因无继承milestone进入此流程,确定
后需同步更新已有根Issue的milestone(避免仅子Issue有milestone而根Issue未设置的不一致情况)。
bash
if [[ -n "${ROOT_NUMBER}" && -n "${MILESTONE}" ]]; then
gh issue edit "${ROOT_NUMBER}" --milestone "${MILESTONE}"
fi
Step 3: ルート(トラッキング)issue を作成する
Step 3: 创建根(追踪)Issue
ルート issue はツリー全体の進捗を管理するトラッキング issue として作成する。ルート issue 自体には phase ラベルは付与しない(Phase 親以下の issue にのみ付与する)。
指定時は新規作成をスキップする。 での 2 回目以降の部分起票で
ルート issue を重複作成しないため、Step 2.5 で設定・OPEN 検証済みの
を
そのまま再利用する(ここで再代入・再検証しない)。
未指定の場合のみ、以下でルート issue を新規作成する。
根Issue作为管理整个树进度的追踪Issue创建。根Issue本身不添加phase标签(仅Phase父及以下的Issue添加)。
指定时跳过新建操作。 为避免在
的第二次及以后部分发起时重复创建根Issue,直接复用Step 2.5中已设置并验证OPEN状态的
(此处不再重复赋值与验证)。
MILESTONE が空でなければ --milestone を付与する(Step 2.5 で決定済み)
若MILESTONE不为空,则添加--milestone参数(Step 2.5已确定)
ROOT_ARGS=(--title "chore: 全 open issue の Phase 別トラッキング ($(date +%Y-%m-%d))")
if [[ -n "${MILESTONE}" ]]; then
ROOT_ARGS+=(--milestone "${MILESTONE}")
fi
ROOT_ARGS=(--title "chore: 所有open Issue的Phase别追踪 ($(date +%Y-%m-%d))")
if [[ -n "${MILESTONE}" ]]; then
ROOT_ARGS+=(--milestone "${MILESTONE}")
fi
gh issue create は issue URL を stdout に出力する(--json 非対応)。URL 末尾から番号を抽出する
gh issue create会将Issue URL输出到stdout(不支持--json)。从URL末尾提取编号
ROOT_URL=$(gh issue create "${ROOT_ARGS[@]}"
--body "$(cat <<'EOF'
ROOT_URL=$(gh issue create "${ROOT_ARGS[@]}"
--body "$(cat <<'EOF'
全 open issue を Phase 別に 1 ツリーへ整理する。各 Phase 親 issue を sub-issues として紐付け。
将所有open Issue按Phase整理为一个树结构。各Phase父Issue作为sub-issues关联到根Issue。
| Phase | 親 issue | 直下 | 総 open 件数 |
|---|
| (作成後に更新) | | | |
| Phase | 父Issue | 直接子Issue数 | 总open数量 |
|---|
| (创建后更新) | | | |
- 新規 issue は起票時に Phase 親へ紐付ける
- 実行順は sub-issues リスト順が正
- closed 親の下に open issue を残置しない
- implement-issue-tree が post-order DFS で消化可能な構造を維持する
EOF
)")
ROOT_NUMBER=$(printf '%s' "${ROOT_URL}" | grep -oE '[0-9]+$')
echo "ルート issue: ${ROOT_NUMBER}"
- 新Issue发起时需关联到对应Phase父Issue
- 执行顺序以sub-issues列表顺序为准
- 已关闭的父Issue下不得遗留open Issue
- 维持可被implement-issue-tree通过后序DFS处理的结构
EOF
)")
ROOT_NUMBER=$(printf '%s' "${ROOT_URL}" | grep -oE '[0-9]+$')
echo "根Issue: ${ROOT_NUMBER}"
Step 4: Phase 親 issue を作成して紐付ける
Step 4: 创建Phase父Issue并关联
Phase ごとに親 issue を作成し、sub_issues API でルートへ紐付ける。
以下のスニペットは
変数で Phase 番号を切り替える。
指定時はその番号を、
全 Phase 起票時は処理中の Phase 番号を設定する(タイトル・ラベルとも
に追従させる)。
为每个Phase创建父Issue,通过sub_issues API关联到根Issue。
以下代码片段通过
变量切换Phase编号。指定
时设置为对应编号,全Phase发起时设置为当前处理的Phase编号(标题与标签均跟随
)。
処理対象の Phase 番号(--phase 指定時はその番号、全 Phase 起票時はループ中の番号)
当前处理的Phase编号(指定--phase时为该编号,全Phase发起时为循环中的编号)
phase ラベルが存在しないリポジトリでは issue 作成が失敗するため、必ず事前作成する
若仓库中不存在phase标签,Issue创建会失败,因此必须提前创建
(作成済みの場合は失敗を無視して続行する)
(若已存在则忽略错误继续执行)
gh label create "phase:${PHASE}" --color "0075ca" 2>/dev/null || true
gh label create "phase:${PHASE}" --color "0075ca" 2>/dev/null || true
--root 再実行時の重複防止: ルート直下に同じ Phase の open な親が既にあれば再利用する
避免--root重复执行时创建重复项:若根Issue下已存在同Phase的open父Issue,则复用
closed な親は再利用しない(closed 親の下に open issue を残置しない運用ルールと整合させる)。
不复用已关闭的父Issue(与「已关闭父Issue下不得遗留open Issue」的运营规则保持一致)。
phase ラベルだけでは同ラベルの一般 issue がルート直下に混在した場合に誤マッチするため、
仅靠phase标签可能会误匹配根Issue下的普通同标签Issue,因此同时通过Phase父Issue的标题规则(feat(phase-N):前缀)筛选。
Phase 親のタイトル規約(feat(phase-N): 接頭辞)でも絞り込む。
为应对子Issue超过100件的情况,通过分页遍历所有子Issue
直下が 100 件を超える場合に備えてページングで全件走査する
PHASE_NUMBER=""
PAGE=1
while true; do
RESULT=$(gh api
"repos/{owner}/{repo}/issues/${ROOT_NUMBER}/sub_issues?per_page=100&page=${PAGE}")
PHASE_NUMBER=$(echo "${RESULT}" | jq -r
"[.[] | select(.state == "open"
and (.title | startswith("feat(phase-${PHASE}):"))
and any(.labels[]?; .name == "phase:${PHASE}"))][0].number // empty")
COUNT=$(echo "${RESULT}" | jq 'length')
if [[ -n "${PHASE_NUMBER}" || "${COUNT}" -lt 100 ]]; then break; fi
PAGE=$((PAGE + 1))
done
[[ -n "${PHASE_NUMBER}" ]] && echo "既存の Phase 親 issue を再利用: #${PHASE_NUMBER}"
PHASE_NUMBER=""
PAGE=1
while true; do
RESULT=$(gh api
"repos/{owner}/{repo}/issues/${ROOT_NUMBER}/sub_issues?per_page=100&page=${PAGE}")
PHASE_NUMBER=$(echo "${RESULT}" | jq -r
"[.[] | select(.state == "open"
and (.title | startswith("feat(phase-${PHASE}):"))
and any(.labels[]?; .name == "phase:${PHASE}"))][0].number // empty")
COUNT=$(echo "${RESULT}" | jq 'length')
if [[ -n "${PHASE_NUMBER}" || "${COUNT}" -lt 100 ]]; then break; fi
PAGE=$((PAGE + 1))
done
[[ -n "${PHASE_NUMBER}" ]] && echo "复用已有的Phase父Issue: #${PHASE_NUMBER}"
再利用する Phase 親が milestone 未設定の場合はここで揃える(milestone 導入前に
若复用的Phase父Issue未设置milestone,在此处统一设置(避免给milestone引入前创建的树追加Issue时,仅新子Issue有milestone的不一致问题。此操作幂等)
作られたツリーへの追記で、新規の子だけに milestone が付く不整合を防ぐ。冪等)
if [[ -n "${PHASE_NUMBER}" && -n "${MILESTONE}" ]]; then
gh issue edit "${PHASE_NUMBER}" --milestone "${MILESTONE}"
fi
タイトル規約が `feat(phase-N):` と異なるツリーでは上記の自動判定に頼らず、候補をユーザーに
提示して再利用すべき Phase 親を確認する。
`PHASE_NUMBER` が空(既存の Phase 親がない)場合のみ、以下で新規作成してルートへ紐付ける。
```bash
if [[ -n "${PHASE_NUMBER}" && -n "${MILESTONE}" ]]; then
gh issue edit "${PHASE_NUMBER}" --milestone "${MILESTONE}"
fi
若树的标题规则与`feat(phase-N):`不同,则不依赖上述自动判定,而是将候选Issue展示给用户,确认需复用的Phase父Issue。
仅当`PHASE_NUMBER`为空(无已存在的Phase父Issue)时,按以下步骤新建并关联到根Issue:
```bash
MILESTONE が空でなければ --milestone を付与する(Step 2.5 で決定済み)
若MILESTONE不为空,则添加--milestone参数(Step 2.5已确定)
PHASE_ARGS=(--title "feat(phase-${PHASE}): Phase ${PHASE} 基盤整備" --label "phase:${PHASE}")
if [[ -n "${MILESTONE}" ]]; then
PHASE_ARGS+=(--milestone "${MILESTONE}")
fi
PHASE_ARGS=(--title "feat(phase-${PHASE}): Phase ${PHASE} 基础架构搭建" --label "phase:${PHASE}")
if [[ -n "${MILESTONE}" ]]; then
PHASE_ARGS+=(--milestone "${MILESTONE}")
fi
Phase 親 issue を作成(URL 末尾から番号を抽出)
创建Phase父Issue(从URL末尾提取编号)
PHASE_URL=$(gh issue create "${PHASE_ARGS[@]}"
--body "$(cat <<'EOF'
PHASE_URL=$(gh issue create "${PHASE_ARGS[@]}"
--body "$(cat <<'EOF'
この Phase の実装タスクをまとめる親 issue。
| Issue | タイトル | 分解 |
|---|
| (子 issue 作成後に更新) | | |
| EOF | | |
| )") | | |
| PHASE_NUMBER=$(printf '%s' "${PHASE_URL}" | grep -oE '[0-9]+$') | |
| Issue | 标题 | 分解情况 |
|---|
| (子Issue创建后更新) | | |
| EOF | | |
| )") | | |
| PHASE_NUMBER=$(printf '%s' "${PHASE_URL}" | grep -oE '[0-9]+$') | |
ルートへ紐付け。sub_issue_id は issue 番号ではなく database id を渡す(GitHub sub-issues API 仕様)
关联到根Issue。sub_issue_id需传入database id而非Issue编号(GitHub sub-issues API规范)
PHASE_ID=$(gh api "repos/{owner}/{repo}/issues/${PHASE_NUMBER}" --jq '.id')
gh api
--method POST
"repos/{owner}/{repo}/issues/${ROOT_NUMBER}/sub_issues"
-F "sub_issue_id=${PHASE_ID}"
PHASE_ID=$(gh api "repos/{owner}/{repo}/issues/${PHASE_NUMBER}" --jq '.id')
gh api
--method POST
"repos/{owner}/{repo}/issues/${ROOT_NUMBER}/sub_issues"
-F "sub_issue_id=${PHASE_ID}"
Step 5: 子 issue・sub-issue を作成して紐付ける
Step 5: 创建子Issue・sub-issue并关联
各タスクを issue として作成し、Phase 親へ紐付ける。4h 超のタスクはさらに sub-issue に分解する。
将每个任务创建为Issue,关联到对应Phase父Issue。超过4小时工作量的任务需进一步分解为sub-issue。
MILESTONE が空でなければ --milestone を付与する(Step 2.5 で決定済み)
若MILESTONE不为空,则添加--milestone参数(Step 2.5已确定)
CHILD_ARGS=(--title "feat: タスク名" --label "phase:${PHASE}")
if [[ -n "${MILESTONE}" ]]; then
CHILD_ARGS+=(--milestone "${MILESTONE}")
fi
CHILD_ARGS=(--title "feat: 任务名称" --label "phase:${PHASE}")
if [[ -n "${MILESTONE}" ]]; then
CHILD_ARGS+=(--milestone "${MILESTONE}")
fi
子 issue を作成(URL 末尾から番号を抽出)。PHASE は Step 4 で設定した番号を引き継ぐ
创建子Issue(从URL末尾提取编号)。PHASE沿用Step 4设置的编号
CHILD_URL=$(gh issue create "${CHILD_ARGS[@]}"
--body "$(cat <<'EOF'
CHILD_URL=$(gh issue create "${CHILD_ARGS[@]}"
--body "$(cat <<'EOF'
Phase 親へ紐付け(sub_issue_id は database id)
关联到Phase父Issue(sub_issue_id需传入database id)
CHILD_ID=$(gh api "repos/{owner}/{repo}/issues/${CHILD_NUMBER}" --jq '.id')
gh api
--method POST
"repos/{owner}/{repo}/issues/${PHASE_NUMBER}/sub_issues"
-F "sub_issue_id=${CHILD_ID}"
CHILD_ID=$(gh api "repos/{owner}/{repo}/issues/${CHILD_NUMBER}" --jq '.id')
gh api
--method POST
"repos/{owner}/{repo}/issues/${PHASE_NUMBER}/sub_issues"
-F "sub_issue_id=${CHILD_ID}"
4h 超の場合は sub-issue を作成して子 issue へ紐付け(同じく MILESTONE を付与)
若任务超过4小时,则创建sub-issue并关联到子Issue(同样添加MILESTONE)
SUB_ARGS=(--title "feat: サブタスク名" --label "phase:${PHASE}")
if [[ -n "${MILESTONE}" ]]; then
SUB_ARGS+=(--milestone "${MILESTONE}")
fi
SUB_URL=$(gh issue create "${SUB_ARGS[@]}" --body "...")
SUB_NUMBER=$(printf '%s' "${SUB_URL}" | grep -oE '[0-9]+$')
SUB_ID=$(gh api "repos/{owner}/{repo}/issues/${SUB_NUMBER}" --jq '.id')
gh api
--method POST
"repos/{owner}/{repo}/issues/${CHILD_NUMBER}/sub_issues"
-F "sub_issue_id=${SUB_ID}"
issue 数が多い場合(50 件超が目安)は `per_page=100` パラメータを使用し、ページネーションで全件確認する。
```bash
SUB_ARGS=(--title "feat: 子任务名称" --label "phase:${PHASE}")
if [[ -n "${MILESTONE}" ]]; then
SUB_ARGS+=(--milestone "${MILESTONE}")
fi
SUB_URL=$(gh issue create "${SUB_ARGS[@]}" --body "...")
SUB_NUMBER=$(printf '%s' "${SUB_URL}" | grep -oE '[0-9]+$')
SUB_ID=$(gh api "repos/{owner}/{repo}/issues/${SUB_NUMBER}" --jq '.id')
gh api
--method POST
"repos/{owner}/{repo}/issues/${CHILD_NUMBER}/sub_issues"
-F "sub_issue_id=${SUB_ID}"
若Issue数量较多(参考标准:超过50件),需使用`per_page=100`参数,通过分页遍历所有Issue。
```bash
ページネーション例(全 sub-issues を取得)
分页示例(获取所有sub-issues)
PAGE=1
while true; do
RESULT=$(gh api
"repos/{owner}/{repo}/issues/${PHASE_NUMBER}/sub_issues?per_page=100&page=${PAGE}")
COUNT=$(echo "${RESULT}" | jq 'length')
echo "${RESULT}"
if [ "${COUNT}" -lt 100 ]; then break; fi
PAGE=$((PAGE + 1))
done
PAGE=1
while true; do
RESULT=$(gh api
"repos/{owner}/{repo}/issues/${PHASE_NUMBER}/sub_issues?per_page=100&page=${PAGE}")
COUNT=$(echo "${RESULT}" | jq 'length')
echo "${RESULT}"
if [ "${COUNT}" -lt 100 ]; then break; fi
PAGE=$((PAGE + 1))
done
Step 6: ルート issue 本文を Phase 別表で更新する
Step 6: 更新根Issue正文的Phase别表格
全 issue の作成が完了したら、ルート issue 本文の Phase 別表を実際の issue 番号・件数で更新する。
での部分起票では本文を全置換しない。 既存ルートの本文には先行 Phase の表が
含まれるため、現在の本文を取得し、今回起票した Phase の行・セクションのみを追記・更新した
本文で
する。以下の全置換テンプレートは新規作成(
未指定)時のみ使う。
所有Issue创建完成后,将根Issue正文的Phase别表格更新为实际的Issue编号与数量。
通过进行部分发起时,不替换整个正文。 已有根Issue的正文包含之前Phase的表格,因此需获取当前正文,仅追加/更新本次发起的Phase行与章节,再通过
更新。以下全替换模板仅在新建(未指定
)时使用。
--root 指定時: 既存本文を取得し、今回の Phase 分をマージしてから編集する
指定--root时:获取已有正文,合并本次Phase内容后再编辑
CURRENT_BODY=$(gh issue view "${ROOT_NUMBER}" --json body --jq '.body')
CURRENT_BODY=$(gh issue view "${ROOT_NUMBER}" --json body --jq '.body')
CURRENT_BODY の「Phase 別実装計画」表へ今回の Phase 行を追記し、
在CURRENT_BODY的「Phase别实施计划」表格中追加本次Phase的行,并添加「### Phase N」章节,组合成新正文后传入gh issue edit --body。
「### Phase N」セクションを追加した本文を組み立てて gh issue edit --body に渡す。
若需要盘点已有树,也可委托给update-issue-tree处理
既存ツリーの棚卸しを伴う場合は update-issue-tree への委譲でもよい
`--root` 未指定(新規作成)の場合は以下で全体を更新する。
```bash
gh issue edit "${ROOT_NUMBER}" --body "$(cat <<'EOF'
未指定`--root`(新建)时,按以下步骤更新整个正文:
```bash
gh issue edit "${ROOT_NUMBER}" --body "$(cat <<'EOF'
全 open issue を Phase 別に 1 ツリーへ整理する。各 Phase 親 issue を sub-issues として紐付け。
将所有open Issue按Phase整理为一个树结构。各Phase父Issue作为sub-issues关联到根Issue。
| Phase | 親 issue | 直下 | 総 open 件数 |
|---|
| Phase 1 | #<phase1_number> タイトル | N | N |
| Phase 2 | #<phase2_number> タイトル | N | N |
| Phase | 父Issue | 直接子Issue数 | 总open数量 |
|---|
| Phase 1 | #<phase1_number> 标题 | N | N |
| Phase 2 | #<phase2_number> 标题 | N | N |
Phase 1: 基盤整備
Phase 1: 基础架构搭建
| Issue | タイトル | 分解 |
|---|
| #N | タイトル | - |
| #N | タイトル | sub-issue あり |
| Issue | 标题 | 分解情况 |
|---|
| #N | 标题 | - |
| #N | 标题 | 存在sub-issue |
- 新規 issue は起票時に Phase 親へ紐付ける
- 実行順は sub-issues リスト順が正
- closed 親の下に open issue を残置しない
- implement-issue-tree が post-order DFS で消化可能な構造を維持する
EOF
)"
- 新Issue发起时需关联到对应Phase父Issue
- 执行顺序以sub-issues列表顺序为准
- 已关闭的父Issue下不得遗留open Issue
- 维持可被implement-issue-tree通过后序DFS处理的结构
EOF
)"
Step 7: 作成結果を報告する
Step 7: 报告创建结果
create-issue-tree 完了レポート
create-issue-tree 完成报告
| Phase | 親 issue | 起票数 |
|---|
| Phase 1 | #N | N 件 |
| Phase | 父Issue | 发起数量 |
|---|
| Phase 1 | #N | N 件 |
- #N: タイトル(Phase 1 / 親: #M)
...
- #N: 标题(Phase 1 / 父Issue: #M)
...
- 実装消化: implement-issue-tree スキルに ルート issue 番号を渡す
- ツリー更新: update-issue-tree スキルでトラッキング issue を棚卸しする
- 任务落地:将根Issue编号传入implement-issue-tree技能
- 树更新:使用update-issue-tree技能盘点追踪Issue
- ルート issue の sub-issues に各 Phase 親 issue が列挙されていることを確認する
- 各 Phase 親 issue の sub-issues に子 issue が列挙されていることを確認する
gh issue view "${ROOT_NUMBER}"
でルート issue 本文の Phase 別表が正しく生成されていることを確認する
- を割り当てた場合、
gh issue view <N> --json milestone --jq '.milestone.title'
でルート・Phase 親・子いずれも と一致することを確認する
- 确认根Issue的sub-issues中列出了各Phase父Issue
- 确认各Phase父Issue的sub-issues中列出了子Issue
- 通过
gh issue view "${ROOT_NUMBER}"
确认根Issue正文的Phase别表格生成正确
- 若分配了,通过
gh issue view <N> --json milestone --jq '.milestone.title'
确认根Issue、Phase父Issue、子Issue的milestone均与一致
sub-issues は 1 ページ最大 100 件。100 件超のツリーは Step 5 のページネーション
sub-issues每页最多100件。超过100件的树需通过Step 5的分页
ループで全件確認する(以下は per_page=100 で先頭ページのみ。件数が 100 未満なら全件)
循环遍历所有Issue(以下为per_page=100时仅获取第一页。若数量少于100则为全部)
ルート直下の sub-issues を確認
确认根Issue下的sub-issues
gh api "repos/{owner}/{repo}/issues/${ROOT_NUMBER}/sub_issues?per_page=100" --jq '.[].number'
gh api "repos/{owner}/{repo}/issues/${ROOT_NUMBER}/sub_issues?per_page=100" --jq '.[].number'
Phase 親直下の sub-issues を確認
确认Phase父Issue下的sub-issues
gh api "repos/{owner}/{repo}/issues/${PHASE_NUMBER}/sub_issues?per_page=100" --jq '.[].number'
gh api "repos/{owner}/{repo}/issues/${PHASE_NUMBER}/sub_issues?per_page=100" --jq '.[].number'
phase ラベルの同期確認(Phase 親・子 issue にラベルが付いているか確認)
确认phase标签同步(Phase父Issue、子Issue均已添加标签)
gh api "repos/{owner}/{repo}/issues/${PHASE_NUMBER}/sub_issues?per_page=100"
--jq '.[] | {number: .number, labels: [.labels[].name]}'
gh api "repos/{owner}/{repo}/issues/${PHASE_NUMBER}/sub_issues?per_page=100"
--jq '.[] | {number: .number, labels: [.labels[].name]}'
| 問題 | 回避策 |
|---|
| を指定せず 2 回目の部分起票でルート issue が重複作成される | 2 回目以降は必ず を渡す |
| に issue 番号をそのまま渡す | gh api .../issues/<number> --jq '.id'
で database id を取得してから POST する |
| phase ラベルが存在しないリポジトリで issue 作成が失敗する | Step 4 冒頭の gh label create "phase:${PHASE}"
を必ず先に実行する |
| 追記時に既存ルートの milestone が未設定なのに気づかず milestone なしで起票してしまう | リポジトリに milestone が存在する場合、Step 2.5 は継承結果が空ならユーザー確認フローへ自動的に合流する(確認で milestone を選ぶとルート issue にも反映される)。milestone が 1 件もない非運用リポジトリでは非運用ガードによる milestone なし起票が正常動作 |
| closed 親の下に open issue が残置される | Phase 親を close する前に全子 issue の close を確認する |
| 问题 | 解决方法 |
|---|
| 未指定进行第二次部分发起时,重复创建了根Issue | 第二次及以后发起必须传入 |
| 直接将Issue编号传入 | 通过gh api .../issues/<number> --jq '.id'
获取database id后再POST |
| 仓库中不存在phase标签导致Issue创建失败 | 必须先执行Step 4开头的gh label create "phase:${PHASE}"
|
| 通过追加Issue时,未注意到已有根Issue未设置milestone,导致无milestone发起 | 若仓库存在milestone,Step 2.5会在继承结果为空时自动进入用户确认流程(用户选择milestone后会同步更新根Issue)。无任何milestone的非运营仓库会通过非运营防护正常发起无milestone的Issue |
| 已关闭的父Issue下遗留了open Issue | 关闭Phase父Issue前需确认所有子Issue已关闭 |
- 1 issue は 4h 程度に収める。 4h を超えると判断した場合は sub-issue に分解する
- issue タイトルは Conventional Commits 形式を推奨(・・ 等)
- 指定で部分起票した場合、別 Phase の追加起票では 必ず を渡す(Step 3 の新規作成をスキップして既存ツリーへ継ぎ足し、Step 6 も全置換せず既存本文へ差分追記する)。起票後は update-issue-tree に同じルート issue 番号を渡して棚卸しする
- ページネーション: sub-issues が 100 件を超える場合は でページングして全件取得する
- シェルコマンドの変数は必ず でクォートする(コマンドインジェクション対策)
- は絶対に使用しない
- は 非対応。issue URL を stdout に出力するため、 で末尾の番号を抽出して変数に保持する
- sub_issues API の は issue 番号ではなく database id(GitHub 仕様)。
gh api "repos/{owner}/{repo}/issues/<number>" --jq '.id'
で id を取得してから POST する。番号をそのまま渡すと誤った issue を紐付ける/404 になる
- phase ラベルは Step 4 冒頭の
gh label create "phase:${PHASE}" --color "0075ca"
で issue 作成より前に必ず作成する(作成済みリポジトリでは no-op)
- 1个Issue的工作量控制在4小时左右。 若判断超过4小时,需分解为sub-issue
- Issue标题推荐使用Conventional Commits格式(・・等)
- 通过指定部分发起后,追加其他Phase时必须传入(跳过Step 3的新建操作,追加到已有树中,Step 6也不会全替换正文而是追加差分)。发起后需将同一根Issue编号传入update-issue-tree进行盘点
- 分页处理:若sub-issues超过100件,需使用分页获取所有Issue
- 脚本变量必须用包裹(防止命令注入)
- 绝对禁止使用
- 不支持。会将Issue URL输出到stdout,需通过提取末尾的编号并保存到变量中
- sub_issues API的需传入database id而非Issue编号(GitHub规范)。通过
gh api "repos/{owner}/{repo}/issues/<number>" --jq '.id'
获取id后再POST。直接传入编号会导致关联错误的Issue或返回404
- phase标签必须在Issue创建前通过Step 4开头的
gh label create "phase:${PHASE}" --color "0075ca"
创建(已存在的仓库中此操作为空操作)