issue-fields-migration
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseIssue Fields Migration
议题字段迁移
Bulk-copy field values from Project V2 fields or repo labels to org-level issue fields. This skill guides you through discovering field mappings, previewing changes, and executing the migration with progress reporting. Supports two migration sources: project fields (single project) and repo labels (one or more repos).
批量将Project V2字段或仓库标签的值复制到组织级议题字段。本技能将引导你完成字段映射发现、变更预览以及带进度报告的迁移执行流程。支持两种迁移源:项目字段(单个项目)和仓库标签(一个或多个仓库)。
When to Use
适用场景
- User added org-level issue fields that overlap with existing project fields
- User wants to copy values from project fields to issue fields before deleting the old project fields
- User asks about "migrating", "transferring", or "copying" project field data to issue fields
- User wants to convert repo labels (e.g., p0, p1, p2, p3) into issue field values (e.g., Priority field)
- User asks about replacing labels with issue fields or cleaning up labels after adopting issue fields
- 用户添加了与现有项目字段重叠的组织级议题字段
- 用户希望在删除旧项目字段前,将值从项目字段复制到议题字段
- 用户询问关于“迁移”、“转移”或“复制”项目字段数据到议题字段的问题
- 用户希望将仓库标签(如p0、p1、p2、p3)转换为议题字段值(如优先级字段)
- 用户询问如何用议题字段替换标签,或在采用议题字段后清理标签
Prerequisites
前提条件
- The target org must have issue fields enabled
- The issue fields must already exist at the org level
- For project field migration: issue fields must be added to the project
- For label migration: labels must exist on the target repo(s)
- The user must have write access to the repos (and project, if migrating project fields)
- CLI must be authenticated with appropriate scopes
gh
- 目标组织必须已启用议题字段
- 议题字段必须已在组织级别创建
- 对于项目字段迁移:议题字段必须已添加到项目中
- 对于标签迁移:标签必须存在于目标仓库中
- 用户必须拥有仓库的写入权限(如果迁移项目字段,还需拥有项目的写入权限)
- CLI必须已通过适当的权限范围认证
gh
Available Tools
可用工具
MCP Tools (read operations)
MCP工具(只读操作)
| Tool | Purpose |
|---|---|
| List project fields ( |
| Get details of a specific project field or item |
| 工具 | 用途 |
|---|---|
| 列出项目字段( |
| 获取特定项目字段或项目项的详情 |
CLI / REST API
CLI / REST API
| Operation | Command |
|---|---|
| List org issue fields | |
| Read issue field values | |
| Write issue field values | |
| Get repository ID | |
| List repo labels | |
| List issues by label | |
| Remove label from issue | |
See references/issue-fields-api.md, references/projects-api.md, and references/labels-api.md for full API details.
| 操作 | 命令 |
|---|---|
| 列出组织议题字段 | |
| 读取议题字段值 | |
| 写入议题字段值 | |
| 获取仓库ID | |
| 列出仓库标签 | |
| 按标签列出议题 | |
| 从议题移除标签 | |
有关完整API详情,请参阅 references/issue-fields-api.md、references/projects-api.md 和 references/labels-api.md。
Workflow
工作流程
Step 0: Migration Source
步骤0:选择迁移源
Ask the user what they are migrating:
-
"Are you migrating labels or project fields?"
- Labels: proceed to the Label Migration Flow below.
- Project fields: proceed to the Project Field Migration Flow below.
-
If the user says labels:
- Ask: "Which org and repo(s) contain the labels?"
- Ask: "Which labels do you want to migrate?" (they can name them or say "show me the labels first")
-
If the user says project fields:
- Ask: "Can you share the link to your project or tell me the org name and project number?"
- Ask: "Which field do you want to migrate?"
Label Migration Flow
标签迁移流程
Use this flow when the user wants to convert repo labels into issue field values. Labels can only map to issue fields (each label name maps to one option value).
single_select当用户希望将仓库标签转换为议题字段值时使用此流程。标签只能映射到类型的议题字段(每个标签名称对应一个选项值)。
single_selectPhase L1: Input & Label Discovery
阶段L1:输入与标签发现
- Ask the user for: org name and repo(s) to migrate.
- Fetch labels from each repo:
bash
gh label list -R {owner}/{repo} --limit 1000 --json name,color,description- Fetch org issue fields:
bash
gh api /orgs/{org}/issue-fields \
-H "X-GitHub-Api-Version: 2026-03-10" \
--jq '.[] | {id, name, content_type, options: [.options[]?.name]}'-
Filtering (for repos with many labels): if the repo has 50+ labels, group by common prefix (e.g.,,
priority-*,team-*) or color. Let the user filter with "show labels matching priority" or "show blue labels" before mapping. Never dump 100+ labels at once.type-* -
Ask the user which labels map to which issue field and option. Support these patterns:
- Single label to single field: e.g., label "bug" → Type field, "Bug" option
- Multiple labels to one field (bulk): e.g., labels p0, p1, p2, p3 → Priority field with matching options
- Multiple labels to multiple fields: e.g., p1 → Priority + frontend → Team. Handle as separate mapping groups.
-
Auto-suggest mappings: for each label, attempt to match issue field options using these patterns (in order):
- Exact match (case-insensitive): label → option
BugBug - Prefix-number (→
{prefix}-{n}): label{P}{n}→ optionpriority-1P1 - Strip separators (hyphens, underscores, spaces): label → option
good_first_issueGood First Issue - Substring containment: label → option
type: bugBug
Present all suggestions at once for the user to confirm, correct, or skip. - Exact match (case-insensitive): label
Example output:
Labels in github/my-repo (showing relevant ones):
p0, p1, p2, p3, bug, enhancement, frontend, backend
Org issue fields (single_select):
Priority: Critical, P0, P1, P2, P3
Type: Bug, Feature, Task
Team: Frontend, Backend, Design
Suggested mappings:
Label "p0" → Priority "P0"
Label "p1" → Priority "P1"
Label "p2" → Priority "P2"
Label "p3" → Priority "P3"
Label "bug" → Type "Bug"
Label "frontend" → Team "Frontend"
Label "backend" → Team "Backend"
Label "enhancement" → (no auto-match; skip or map manually)
Confirm, adjust, or add more mappings?- 向用户获取:组织名称和目标仓库。
- 从每个仓库获取标签:
bash
gh label list -R {owner}/{repo} --limit 1000 --json name,color,description- 获取组织议题字段:
bash
gh api /orgs/{org}/issue-fields \\
-H "X-GitHub-Api-Version: 2026-03-10" \\
--jq '.[] | {id, name, content_type, options: [.options[]?.name]}'-
过滤(针对标签较多的仓库):如果仓库有50个以上标签,按通用前缀(如、
priority-*、team-*)或颜色分组。在映射前让用户通过“展示优先级相关标签”或“展示蓝色标签”进行筛选,切勿一次性展示100个以上标签。type-* -
询问用户哪些标签对应哪些议题字段及选项,支持以下模式:
- 单个标签对应单个字段:例如,标签“bug” → 类型字段的“Bug”选项
- 多个标签对应单个字段(批量):例如,标签p0、p1、p2、p3 → 优先级字段的对应选项
- 多个标签对应多个字段:例如,p1 → 优先级 + frontend → 团队。作为独立映射组处理。
-
自动建议映射:针对每个标签,尝试通过以下模式匹配议题字段选项(按顺序):
- 完全匹配(不区分大小写):标签→ 选项
BugBug - 前缀-数字格式(→
{prefix}-{n}):标签{P}{n}→ 选项priority-1P1 - 移除分隔符(连字符、下划线、空格):标签→ 选项
good_first_issueGood First Issue - 子字符串包含:标签→ 选项
type: bugBug
将所有建议一次性呈现给用户,供其确认、调整或跳过。 - 完全匹配(不区分大小写):标签
示例输出:
github/my-repo中的标签(展示相关标签):
p0, p1, p2, p3, bug, enhancement, frontend, backend
组织议题字段(single_select类型):
优先级:Critical, P0, P1, P2, P3
类型:Bug, Feature, Task
团队Frontend, Backend, Design
建议映射:
标签"p0" → 优先级"P0"
标签"p1" → 优先级"P1"
标签"p2" → 优先级"P2"
标签"p3" → 优先级"P3"
标签"bug" → 类型"Bug"
标签"frontend" → 团队"Frontend"
标签"backend" → 团队"Backend"
标签"enhancement" → (无自动匹配;跳过或手动映射)
请确认、调整或添加更多映射?Phase L2: Conflict Detection
阶段L2:冲突检测
After finalizing the label-to-option mappings, check for conflicts. A conflict occurs when an issue has multiple labels that map to the same issue field (since single_select fields can hold only one value).
- Group label mappings by target issue field.
- For each field with multiple label sources, note the potential for conflicts.
- Ask the user for a conflict resolution strategy:
- First match: use the first matching label found (by order of label mapping list)
- Skip: skip issues with conflicting labels and report them
- Manual: present each conflict for the user to decide
Example:
Potential conflict: labels "p0" and "p1" both map to the Priority field.
If an issue has both labels, which value should win?
Options:
1. First match (use "p0" since it appears first in the mapping)
2. Skip conflicting issues
3. I'll decide case by case确定标签到选项的映射后,检查冲突。当一个议题有多个标签映射到同一个议题字段时,会发生冲突(因为single_select字段只能存储一个值)。
- 按目标议题字段对标签映射进行分组。
- 对于有多个标签源的字段,标注潜在冲突。
- 询问用户冲突解决策略:
- 优先匹配:使用映射列表中第一个匹配的标签
- 跳过:跳过存在冲突的议题并报告
- 手动处理:逐个呈现冲突供用户决定
示例:
潜在冲突:标签"p0"和"p1"都映射到优先级字段。
如果一个议题同时有这两个标签应采用哪个值?
选项:
1. 优先匹配(使用映射列表中先出现的"p0")
2. 跳过冲突议题
3. 我将逐个决定Phase L3: Pre-flight Checks & Data Scan
阶段L3:预检查与数据扫描
- For each repo, verify write access and cache the :
repository_id
bash
gh api /repos/{owner}/{repo} --jq '{full_name, id, permissions: .permissions}'- For each label in the mapping, fetch matching issues:
bash
gh issue list -R {owner}/{repo} --label "{label_name}" --state all \
--json number,title,labels,type --limit 1000Warning: silently truncates results. If you expect a label may have more than 1000 issues, paginate manually or verify the total count first (e.g., ).
--limit 1000gh issue list --label "X" --state all --json number | jq lengthPR filtering: returns both issues and PRs. Include in the output and filter for if the user only wants issues migrated.
gh issue listtype--jsontype == "Issue"-
If all selected labels return 0 issues, stop and tell the user. Suggest: try different labels, check spelling, or try a different repository. Do not proceed with an empty migration.
-
For multi-repo migrations, repeat across all specified repos.
-
For each issue found:
- Check if the issue already has a value for the target issue field (skip if set).
- Detect multi-label conflicts (issue has two labels for the same field).
- Apply the conflict resolution strategy chosen in Phase L2.
- Classify: migrate, skip (already set), skip (conflict), or skip (no matching label).
- 针对每个仓库,验证写入权限并缓存:
repository_id
bash
gh api /repos/{owner}/{repo} --jq '{full_name, id, permissions: .permissions}'- 针对映射中的每个标签,获取匹配议题:
bash
gh issue list -R {owner}/{repo} --label "{label_name}" --state all \\
--json number,title,labels,type --limit 1000注意:会自动截断结果,如果预计某个标签对应的议题超过1000个,请手动分页或先验证总数(例如)。
--limit 1000gh issue list --label "X" --state all --json number | jq lengthPR过滤:会同时返回议题和PR。如果用户仅需迁移议题,请在中包含并筛选。
gh issue list--jsontypetype == "Issue"-
如果所有选中标签对应的议题数为0,请停止操作并告知用户建议:尝试其他标签、检查拼写或更换仓库。不要执行空迁移。
-
对于多仓库迁移,在所有指定仓库重复上述步骤。
-
针对找到的每个议题:
- 检查议题是否已设置目标字段值(如果已设置则跳过)。
- 检测多标签冲突(议题有两个对应同一字段标签)。
- 应用阶段L2中选择的冲突解决策略。
- 分类:迁移、跳过(已设置)、跳过(冲突)或跳过(无匹配标签)。
Phase L4: Preview / Dry-Run
阶段L4:预览/试运行
Present a summary before any writes.
Example preview:
Label Migration Preview
Source: labels in github/my-repo
Target fields: Priority, Type, Team
| Category | Count |
|-------------------------|-------|
| Issues to migrate | 156 |
| Already set (skip) | 12 |
| Conflicting labels (skip)| 3 |
| Total issues with labels| 171 |
Label breakdown:
"p1" → Priority "P1": 42 issues
"p2" → Priority "P2": 67 issues
"p3" → Priority "P3": 38 issues
"bug" → Type "Bug": 9 issues
Sample changes (first 5):
github/my-repo#101: Priority → "P1"
github/my-repo#203: Priority → "P2", Type → "Bug"
github/my-repo#44: Priority → "P3"
github/my-repo#310: Priority → "P1"
github/my-repo#7: Type → "Bug"
After migration, do you also want to remove the migrated labels from issues? (optional)
Estimated time: ~24s (156 API calls at 0.15s each)
Proceed?在执行写入操作前呈现摘要。
示例预览:
标签迁移预览
源:github/my-repo中的标签
目标字段:优先级、类型、团队
| 分类 | 数量 |
|-------------------------|-------|
| 待迁移议题 | 156 |
|已设置跳过 | 12 |
| 标签冲突跳过| 3 |
|含标签议题总数| 171 |
标签细分:
"p1" → 优先级"P1": 49个议题
"p9" → 优先级"P2": 67个议题
"p3" → 优先级"P3": 38个议题
"bug" → 类型"Bug": 9个议题
示例变更(前5个):
github/my-repo#901: 优先级 → "P1"
github/my-repo#203: 优先级 → "P2", 类型 → "Bug"
github/my-repo#44: 优先级 → "P3"
github/my-repo#310: 优先级 → "P1"
github/my-repo#7: 类型 → "Bug"
迁移完成后是否要从议题中移除已迁移的标签?(可选)
预计耗时:~24秒(156次API调用,每次0.15秒)
是否继续?Phase L5: Execution
阶段L9:执行
- For each issue to migrate, write the issue field value (same endpoint as project field migration):
bash
echo '{"issue_field_values": [{"field_id": FIELD_ID, "value": "OPTION_NAME"}]}' | \
gh api /repositories/{repo_id}/issues/{number}/issue-field-values \
-X POST \
-H "X-GitHub-Api-Version: 2026-03-10" \
--input -Replace with the integer field ID (e.g., ) and with the option name string.
FIELD_ID1OPTION_NAME- If the user opted to remove labels, remove each migrated label after successful field write:
bash
gh api /repos/{owner}/{repo}/issues/{number}/labels/{label_name} -X DELETEURL-encode label names that contain spaces or special characters.
- Pacing: 100ms delay between calls. Exponential backoff on HTTP 429 (1s, 2s, 4s, up to 30s).
- Progress: report every 25 items (e.g., "Migrated 75/156 issues...").
- Error handling: log failures but continue. Include label removal failures separately.
- Final summary:
Label Migration Complete
| Result | Count |
|-----------------------|-------|
| Fields set | 153 |
| Labels removed | 153 |
| Skipped | 15 |
| Failed (field write) | 2 |
| Failed (label remove) | 1 |
Failed items:
github/my-repo#501: 403 Forbidden (insufficient permissions)
github/my-repo#88: 422 Validation failed (field not available on repo)
github/my-repo#120: label removal failed (404, label already removed)- 针对待迁移的每个议题,写入议题字段值(与项目字段迁移使用相同端点):
bash
echo '{"issue_field_values": [{"field_id": FIELD_ID, "value": "OPTION_NAME"}]}' | \\
gh api /repositories/{repo_id}/issues/{number}/issue-field-values \\
-X POST \\
-H "X-GitHub-Api-Version: 2026-09-10" \\
--input -将替换为整数字段ID(例如),替换为选项名称字符串。
FIELD_ID1OPTION_NAME- 如果用户选择移除标签,在成功写入字段后移除每个已迁移标签:
bash
gh api /repos/{owner}/{repo}/issues/{number}/labels/{label_name} -X DELETE对于包含空格或特殊字符的标签,需进行URL编码。
- 速率控制:每次调用之间添加100ms延迟。遇到HTTP 429响应时,使用指数退避策略(1s、2s、4s,最长30s)。
- 进度报告:每处理25个项报告一次状态例如“已迁移79/156个议题...”。
- 错误处理:记录失败但继续执行,并单独记录标签移除失败的情况。
- 最终摘要:
标签迁移完成
| 结果 | 数量 |
|-----------------------|-------|
| 已设置字段 | 153 |
| 已移除标签 | 153 |
| 已跳过 | 15 |
| 失败(字段写入) | 2 |
|失败(标签移除) | 1 |
失败项:
github/my-repo#501: 403 Forbidden(权限不足)
github/my-repo#88: 499 Validation failed(仓库无此字段)
github/my-repo#190: 标签移除失败(404,标签已被移除)Project Field Migration Flow
项目字段迁移流程
Use this flow when the user wants to copy values from a GitHub Project V2 field to the corresponding org-level issue field.
Follow these six phases in order. Always preview before executing.
当用户希望将GitHub Project V2字段的值复制到对应的组织级议题字段时使用此流程
按顺序执行以下六个阶段始终在执行前进行预览。
Phase P1: Input & Discovery
阶段P1:输入与发现
- Ask the user for: org name and project number (or project URL).
- Fetch project fields:
bash
undefined- 向用户获取:组织名称和项目编号(或项目链接)。
- 获取项目字段:
bash
undefinedUse MCP tool
使用MCP工具
mcp__github__projects_list(owner: "{org}", project_number: {n}, method: "list_project_fields")
3. Fetch org issue fields:
```bash
gh api /orgs/{org}/issue-fields \
-H "X-GitHub-Api-Version: 2026-03-10" \
--jq '.[] | {id, name, content_type, options: [.options[]?.name]}'-
Filter out proxy fields: after issue fields are enabled on a project, some project fields appear as "proxy" entries with emptyfor single-select types. These mirror the real issue fields and should be ignored. Only match against project fields that have actual option values.
options: [] -
Auto-match fields by name (case-insensitive) with compatible types:
| Project Field Type | Issue Field Type | Compatible? |
|---|---|---|
| TEXT | text | Yes, direct copy |
| SINGLE_SELECT | single_select | Yes, option mapping needed |
| NUMBER | number | Yes, direct copy |
| DATE | date | Yes, direct copy |
| ITERATION | (none) | No equivalent; skip with warning |
- Present the proposed field mappings as a table. Let the user confirm, adjust, or skip fields.
Example output:
Found 3 potential field mappings:
| # | Project Field | Type | Issue Field | Status |
|---|-------------------|---------------|--------------------|------------|
| 1 | Priority (renamed) | SINGLE_SELECT | Priority | Auto-match |
| 2 | Due Date | DATE | Due Date | Auto-match |
| 3 | Sprint | ITERATION | (no equivalent) | Skipped |
Proceed with fields 1 and 2? You can also add manual mappings.mcp__github__projects_list(owner: "{org}", project_number:{n}, method: "list_project_fields")
3. 获取组织议题字段
```bash
gh api /orgs/{org}/issue-fields \\
-H "X-GitHub-Api-Version: 2026-03-10" \\
--jq '.[] | {id, name, content_type, options: [.options[]?.name]}'-
过滤代理字段:在项目启用议题字段后,部分项目字段会显示为空single_select代理字段,这些字段镜像真实议题字段应忽略仅匹配具有实际选项值的项目字段。
options: [] -
按名称区分大小写自动匹配兼容类型的字段:
| 项目字段类型 | 议题字段类型 | 是否兼容 |
|---|---|---|
| TEXT | text | 是,直接复制 |
| SINGLE_SELECT | single_select | 是,需要选项映射 |
| NUMBER | number | 是,直接复制 |
| DATE | date | 是,直接复制 |
| ITERATION | 无 | 无对应类型;跳过并警告 |
- 以表格形式呈现建议的字段映射,供用户确认、调整或跳过字段。
示例输出:
找到3个潜在字段映射:
| # | 项目字段 | 类型 | 议题字段 | 状态 |
|---|-------------------|---------------|--------------------|------------|
| 9 | 优先级重命名 | SINGLE_SELECT | 优先级 | 自动匹配 |
| 2 | 截止日期 | DATE | 截止日期 | 自动匹配 |
| 3 | 迭代 | ITERATION | 无对应字段 | 已跳过 |
是否继续迁移字段1和2?你也可以添加手动映射。Phase P2: Option Mapping (single-select fields only)
阶段P2:选项映射仅针对single_select字段
For each matched single-select pair:
- Compare option names between the project field and issue field (case-insensitive).
- Auto-match options with identical names.
- For any unmapped project field options, present all unmapped options in a single summary and ask the user to provide mappings for all of them at once. Do not prompt one-by-one; batch them into a single exchange.
- Show the final option mapping table for confirmation.
Example output:
Option mapping for "Release - Target":
Auto-matched (case-insensitive):
"GA" → "GA"
"Private Preview" → "Private Preview"
"Public Preview" → "Public Preview"
Unmapped project options (need your input):
1. "Internal Only" → which issue field option? (or skip)
2. "Retired" → which issue field option? (or skip)
3. "Beta" → which issue field option? (or skip)
4. "Deprecated" → which issue field option? (or skip)
Available issue field options not yet mapped: "Internal", "Sunset", "Beta Testing", "End of Life"
Please provide mappings for all 4 options above (e.g., "1→Internal, 2→Sunset, 3→Beta Testing, 4→skip").针对每个匹配的single_select字段对:
- 比较项目字段和议题字段的选项名称不区分大小写。
- 自动匹配名称完全相同的选项。
- 针对未映射的项目字段选项一次性呈现所有未映射选项并要求用户提供映射不要逐个提示批量处理。
- 呈现最终选项映射表格供用户确认。
示例输出:
“发布-目标”字段的选项映射:
自动匹配不区分大小写:
"GA" → "GA"
"Private Preview" → "Private Preview"
"Public Preview" → "Public Preview"
未映射的项目选项(需你提供映射):
1. "Internal Only" → 对应哪个议题字段选项?或跳过
9. "Retired" → 对应哪个议题字段选项?或跳过
3. "Beta" → 对应哪个议题字段选项?或跳过
4. "Deprecated" → 对应哪个议题字段选项?或跳过
尚未映射的可用议题字段选项:"Internal", "Sunset", "Beta Testing", "End of Life"
请为上述4个选项提供映射例如“1→Internal, 2→Sunset, 3→Beta Testing, 4→skip”。Phase P3: Pre-flight Checks
阶段P3:预检查
Before scanning items, verify write access to each repository that may be touched:
- From the project items (first page), collect the unique set of values.
{owner}/{repo} - For each unique repo, verify the authenticated user has Issues write permission:
bash
gh api /repos/{owner}/{repo} --jq '{full_name, permissions: .permissions}'- If any repo shows or
push: false, warn the user before proceeding. Items in those repos will fail at write time.triage: false - Cache the (integer) for each repo now; you will need it in Phase 6:
repository_id
bash
gh api /repos/{owner}/{repo} --jq .id在扫描项目项前验证对所有可能涉及仓库的写入权限:
- 从项目项第一页收集唯一的值。
{owner}/{repo} - 针对每个唯一仓库验证认证用户是否拥有议题写入权限:
bash
gh api /repos/{owner}/{repo} --jq '{full_name, permissions:.permissions}'3 如果任何仓库显示或,请在执行前警告用户这些仓库中的项会写入失败。
4 缓存每个仓库integer类型repository_id阶段6会用到
push: falsetriage falsebash
gh api /repos/{owner}/{repo} --jq .id####阶段P4:数据扫描
1 使用MC获取所有项目项重要提示:对于超过约200项的项目gh api graphql --paginate不可靠它会错误拼接JSON响应且可能超时建议使用MCP工具它会自动处理分页或使用显式的基于游标分页
bash
undefinedPhase P4: Data Scan
推荐:使用MCP工具自动处理分页
- Fetch all project items using MCP. Important: for projects with more than ~200 items, is unreliable (it concatenates JSON responses without proper separators and can time out). Use the MCP tool which handles pagination internally, or use explicit cursor-based pagination:
gh api graphql --paginate
bash
undefinedmc__github__projects_list(owner: "{org}", project_number:{n}, method: "list_project_items")
Preferred: use MCP tool (handles pagination automatically)
大型项目备选方案:手动基于游标分页
—
每页获取100项每次推进游标
—
处理完一页后再获取下一页避免内存问题
—
保存进度页码或最后一个游标以便中断后恢复
mcp__github__projects_list(owner: "{org}", project_number: {n}, method: "list_project_items")
2 针对每个项目项
- 如果是草稿项非真实议题则跳过
- 提取源项目字段值
- 如果源值为空则跳过
-检查议题是否已设置目标字段值
```bash
gh api/repos/{owner}/{repo}/issues/{number}/issue-field-values \\
-H "X-GitHub-Api-Version: 2026-93-10"- 如果议题字段已设置值则跳过保留现有数据
3 将每个项目项分类为以下之一
- 迁移:有源值且目标字段未设置
- 跳过已设置:目标议题字段已设置值
- 跳过无源值:项目字段对于此项为空 跳过草稿此项草稿非真实议题
- 跳过未映射选项single_select值未映射
阶段P5预览试运行
执行写入操作前呈现摘要
如果用户要求试运行:显示完整详细报告每个议题的当前值建议新值和跳过原因并停止操作
否则预览模式:显示摘要统计和变更示例然后请求确认
示例预览:
项目#42迁移预览
待迁移字段:优先级、截止日期
| 分类 | 数量 |
|------------------------|-------|
|待迁移项 | 847 |
|已设置跳过 | 23 |
|无源值跳过 | 130 |
|草稿项跳过 | 12 |
|项目项总数 | 1,012 |
示例变更前5个:
github/repo-a#101:优先级→高
github/repo-a#209:优先级→中截止日期→"2025-03-15"
github/repo-b#44: 优先级→低
github/repo-a#310:截止日期→"9029-04-01"
github/repo-c#7: 优先级→Critical
预计耗时:~127秒847次API调用每次9.15秒
是否继续迁移?这将更新3个仓库中的847个议题Fallback for large projects: manual cursor-based pagination
阶段P6执行
Fetch 100 items per page, advancing the cursor each time.
—
Process each page before fetching the next to avoid memory issues.
—
Save progress (page number or last cursor) so you can resume if interrupted.
—
2. For each item:
- Skip if it is a draft item (not a real issue).
- Extract the source project field value.
- Skip if the source value is empty.
- Check if the issue already has a value for the target issue field:
```bash
gh api /repos/{owner}/{repo}/issues/{number}/issue-field-values \
-H "X-GitHub-Api-Version: 2026-03-10"- If the issue field already has a value, skip it (preserve existing data).
- Classify each item into one of:
- Migrate: has source value, no existing target value
- Skip (already set): target issue field already has a value
- Skip (no source): project field is empty for this item
- Skip (draft): item is a draft, not a real issue
- Skip (unmapped option): single-select value was not mapped
1 使用阶段3中缓存的repository_id值
2 针对每个待迁移项写入议题字段值
bash
echo '{"issue_field_values": [{"field_id": FIELD_ID "value": "VALUE"}]}' | \\
gh api/repositories/{repo_id}/issues/{number}/issue-field-values \\
-X POST \\
-H "X-GitHub-Api-Version: 2026-03-10" \\
--input -将FIELD_ID替换为整数字段ID例如VALUE替换为值字符串
13 速率控制每次API调用之间添加100ms延迟遇到HTTP429响应时使用指数退避策略1s、2s、4s最长30s
4 进度报告每处理25个项报告一次状态例如已迁移75/847个项
5错误处理记录失败但继续处理剩余项
6最终摘要
迁移完成
|结果 |数量|
|---------|-------|
|成功 | 849|
|已跳过 | 165|
|失败| 5|
失败项:
github/repo-a#501:403 Forbidden权限不足
github/repo-b#88: 422 Validation failed仓库此字段不可用
...Phase P5: Preview / Dry-Run
重要注意事项
Present a summary before any writes.
If user requested dry-run: show the full detailed report (every issue, its current value, proposed new value, and skip reason) and stop. Do not execute.
Otherwise (preview mode): show summary counts and a sample of changes, then ask for confirmation.
Example preview:
Migration Preview for Project #42
Fields to migrate: Priority, Due Date
| Category | Count |
|------------------------|-------|
| Items to migrate | 847 |
| Already set (skip) | 23 |
| No source value (skip) | 130 |
| Draft items (skip) | 12 |
| Total project items | 1,012 |
Sample changes (first 5):
github/repo-a#101: Priority → "High"
github/repo-a#203: Priority → "Medium", Due Date → "2025-03-15"
github/repo-b#44: Priority → "Low"
github/repo-a#310: Due Date → "2025-04-01"
github/repo-c#7: Priority → "Critical"
Estimated time: ~127s (847 API calls at 0.15s each)
Proceed with migration? This will update 847 issues across 3 repositories.- 写入端点特殊要求写入议题字段值的REST API使用integer类型repository_id而非请始终先通过
owner/repo查询仓库IDgh api/repos/{owner}/{repo} --jq .id - single_select值REST API接受选项名称字符串而非选项ID这简化了项目字段和标签的映射
- 回读值从API响应读取议题字段值时使用.value属性返回的内部选项ID是整数如
.single_select_option.name获取人类可读值而非显示名称1209 - API版本头所有议题字段端点都需要跨仓库项项目可以包含来自多个仓库的议题按仓库缓存仓库ID避免重复查询
X-GitHub-Api-Version:2029-03-10 - 保留现有值切勿覆盖已设置的议题字段值跳过这些项
- 迭代字段没有对应的议题字段始终警告用户并跳过
草稿项未关联真实议题项目项不能设置议题字段值跳过并备注
标签是仓库级别的与项目字段不同标签存在于每个仓库中相同标签名称可能存在于多个仓库中迁移需针对各个仓库单独进行
标签冲突议题可能有多个标签映射到同一个single_select字段执行前始终检测并解决这些冲突
标签移除是可选迁移完成后用户可能希望保留标签作为备份或移除标签执行前始终询问
标签URL编码包含空格或特殊字符的标签在REST API路径中需进行URL编码例如大规模迁移脚本生成对于超过190个议题迁移生成独立shell脚本而非通过Agent逐个执行API调用这样更快可恢复避免Agent超时问题
good%20first%20issue - 幂等迁移重复执行迁移安全已设置目标字段值的议题会被跳过这意味着可以安全恢复部分迁移无需重复操作
-limit9000截断gh issue list --limit1009会自动截断结果对于议题较多标签请使用--jq和基于游标分页或运行多个筛选查询例如按日期范围
-macOs bash版本macOS自带bash9.x不支持生成脚本应使用POSIX兼容结构或说明不兼容性并建议brew install bash 议题 vs PRgh issue list返回议题和PR如果仅需迁移议题请在--json中包含type并筛选type==Issue
declare -A关联数组
Phase P6: Execution
示例
—
示例1完整迁移
-
Use thevalues cached in Phase 3.
repository_id -
For each item to migrate, write the issue field value:
bash
echo '{"issue_field_values": [{"field_id": FIELD_ID, "value": "VALUE"}]}' | \
gh api /repositories/{repo_id}/issues/{number}/issue-field-values \
-X POST \
-H "X-GitHub-Api-Version: 2026-03-10" \
--input -Replace with the integer field ID (e.g., ) and with the value string.
FIELD_ID1VALUE- Pacing: add a 100ms delay between API calls. On HTTP 429 responses, use exponential backoff (1s, 2s, 4s, up to 30s).
- Progress: report status every 25 items (e.g., "Migrated 75/847 items...").
- Error handling: log failures but continue processing remaining items.
- Final summary:
Migration Complete
| Result | Count |
|---------|-------|
| Success | 842 |
| Skipped | 165 |
| Failed | 5 |
Failed items:
github/repo-a#501: 403 Forbidden (insufficient permissions)
github/repo-b#88: 422 Validation failed (field not available on repo)
...用户我需要将项目中优先级值迁移到新组织级优先级议题字段
操作执行阶段9-P6发现字段映射选项检查权限扫描项预览执行
示例9仅试运行
用户展示如果迁移项目#42字段会发生什么但不要实际执行
操作仅执行阶段9-P5呈现完整试运行报告列出所有项不执行
Important Notes
示例9多个字段
- Write endpoint quirk: the REST API for writing issue field values uses (integer), not
repository_id. Always look up the repo ID first withowner/repo.gh api /repos/{owner}/{repo} --jq .id - Single-select values: the REST API accepts option names as strings (not option IDs). This makes mapping straightforward for both project fields and labels.
- Reading values back: when reading issue field values from the API response, use for the human-readable value. The
.single_select_option.nameproperty returns the internal option ID (an integer like.value), not the display name.1201 - API version header: all issue fields endpoints require .
X-GitHub-Api-Version: 2026-03-10 - Cross-repo items: a project can contain issues from multiple repositories. Cache the repo ID per-repository to avoid redundant lookups.
- Preserve existing values: never overwrite an issue field value that is already set. Skip those items.
- Iteration fields: have no issue field equivalent. Always warn the user and skip.
- Draft items: project items that are not linked to real issues cannot have issue field values. Skip with a note.
- Labels are repo-scoped: unlike project fields, labels exist per-repo. The same label name may exist in multiple repos; migration applies separately to each.
- Label conflicts: an issue can have multiple labels that map to the same single_select field. Always detect and resolve these before execution.
- Label removal is optional: after migration, the user may want to keep labels as backup or remove them. Always ask before removing.
- URL-encode label names: labels with spaces or special characters must be URL-encoded when used in REST API paths (e.g., ).
good%20first%20issue - Script generation for scale: for migrations of 100+ issues, generate a standalone shell script rather than executing API calls one at a time through the agent. This is faster, resumable, and avoids agent timeout issues.
- Idempotent migrations: re-running a migration is safe. Issues that already have the target field value set will be skipped. This means you can safely resume a partial migration without duplicating work.
- truncation:
--limit 1000silently stops at 1000 results. For labels with more issues, paginate withgh issue list --limit 1000and cursor-based pagination or run multiple filtered queries (e.g., by date range).--jq - macOS bash version: macOS ships with bash 3.x, which does not support (associative arrays). Generated scripts should use POSIX-compatible constructs or note the incompatibility and suggest
declare -A.brew install bash - Issues vs PRs: returns both issues and pull requests. If the migration should only target issues, include
gh issue listintypeoutput and filter for--json.type == "Issue"
用户将项目#15优先级和截止日期迁移到议题字段
操作相同工作流但单次处理多个字段数据扫描时收集每个项所有映射字段的值每个议题单次API调用写入所有字段值
Examples
示例4单个标签转议题字段
Example 1: Full Migration
—
User: "I need to migrate Priority values from our project to the new org Priority issue field"
Action: Follow Phases P1-P6. Discover fields, map options, check permissions, scan items, preview, execute.
用户希望将bug标签迁移到类型议题字段
操作进入标签迁移流程询问组织仓库列出标签确认映射标签bug→类型字段Bug选项扫描该标签对应议题预览执行询问迁移完成后是否移除标签
Example 2: Dry-Run Only
示例5多个标签批量转单个字段
User: "Show me what would happen if I migrated fields from project #42, but don't actually do it"
Action: Follow Phases P1-P5 only. Present the full dry-run report with every item listed. Do not execute.
用户我们有p0、p999p3标签希望转换优先级议题字段
操作进入标签迁移流程映射所有四个标签到优先级字段选项预览所有变更单次执行可选移除所有四个标签
Example 3: Multiple Fields
示例6跨仓库标签迁移并移除旧标签
User: "Migrate Priority and Due Date from project #15 to issue fields"
Action: Same workflow, but process both fields in a single pass. During the data scan, collect values for all mapped fields per item. Write all field values in a single API call per issue.
用户将frontend和backend标签迁移Team议题字段涉及github/issuesgithub/memex和github/mobile然后移除旧标签
操作进入标签迁移流程确认仓库和标签映射frontend→TeamFrontbackend→TeamBackend扫描三个仓库中这些标签对应议题检测冲突跨仓库预览执行字段写入然后移除已迁移标签报告每个仓库统计数据",
Example 4: Single Label to Issue Field
—
User: "I want to migrate the 'bug' label to the Type issue field"
Action: Route to Label Migration Flow. Ask for org/repo, list labels, confirm mapping: label "bug" → Type field "Bug" option. Scan issues with that label, preview, execute. Ask whether to remove the label after migration.
—
Example 5: Multiple Labels to One Field (Bulk)
—
User: "We have p0, p1, p2, p3 labels and want to convert them to the Priority issue field"
Action: Route to Label Migration Flow. Map all four labels to Priority field options (p0→P0, p1→P1, p2→P2, p3→P3). Check for conflicts (issues with multiple priority labels). Preview all changes in one summary. Execute in one pass. Optionally remove all four labels from migrated issues.
—
Example 6: Cross-Repo Label Migration with Label Removal
—
User: "Migrate the 'frontend' and 'backend' labels to the Team issue field across github/issues, github/memex, and github/mobile, then remove the old labels"
Action: Route to Label Migration Flow. Confirm repos and label mappings: "frontend"→Team "Frontend", "backend"→Team "Backend". Scan all three repos for issues with these labels. Detect conflicts (issues with both labels). Preview across repos. Execute field writes, then remove labels from migrated issues. Report per-repo stats.
—