mcpField
I
issue-fields-migration

issue-fields-migration

github.com/github生产力40.5k1.9k 次安装今天更新
代码仓库

关于 issue-fields-migration

用于将 GitHub 元数据批量迁移到组织级议题字段的技能,支持映射仓库标签或 GitHub Project V2 字段值,并在写入前预览及处理冲突。

主要功能

  1. 01将标签映射到议题字段
  2. 02复制 Project V2 字段值
  3. 03建议字段和选项映射
  4. 04检测标签值冲突
  5. 05迁移前预览变更

适用场景

  1. 01用结构化字段替换优先级标签
  2. 02删除旧字段前转移项目值
  3. 03字段迁移后清理标签

MCP 相关

使用 GitHub MCP 工具读取项目字段和条目;议题字段的读取与写入则通过 GitHub CLI 和 REST API 命令完成。

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/github/awesome-copilot --skill issue-fields-migration

安装后在 Claude Code 中输入/issue-fields-migration确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
labels-api.md
issue-fields-api.md
projects-api.md

详细介绍

Issue Fields 迁移

Issue fields 是组织级类型化元数据(单选、文本、数字、日期),用结构化、可搜索、跨仓库的字段取代了基于标签的变通方案。每个组织都会预配置 Priority、Effort、Start date 和 Target date,并支持最多 25 个自定义字段。

本技能可从两个来源将现有元数据批量迁移到 issue fields:

  • 仓库标签:将 p0、p1、priority/high 等标签转换为结构化的 issue field 值(例如 Priority 字段)。支持一次迁移多个标签,并可在迁移后选择是否移除这些标签。
  • Project V2 字段:将字段值(单选、文本、数字、日期、迭代)从 GitHub Project 复制到对应的组织级 issue fields。

何时使用

  • 用户添加了与现有项目字段重叠的组织级 issue fields
  • 用户希望在删除旧的项目字段之前,将项目字段的值复制到 issue fields
  • 用户询问如何将项目字段数据“迁移”、“转移”或“复制”到 issue fields
  • 用户希望将仓库标签(例如 p0、p1、p2、p3)转换为 issue field 值(例如 Priority 字段)
  • 用户询问如何用 issue fields 取代标签,或在采用 issue fields 后清理标签

前提条件

  • 目标组织必须已启用 issue fields
  • issue fields 必须已在组织级存在
  • 对于项目字段迁移:issue fields 必须已添加到该项目
  • 对于标签迁移:目标仓库上必须存在这些标签
  • 用户必须对相关仓库(以及项目,如果迁移项目字段)具有写入权限
  • gh CLI 必须已通过身份验证并具有相应权限范围

可用工具

MCP 工具(读取操作)
工具用途
mcp__github__projects_list列出项目字段(list_project_fields),列出带值的项目条目(list_project_items)
mcp__github__projects_get获取特定项目字段或条目的详细信息
CLI / REST API
操作命令
列出组织 issue fieldsgh api /orgs/{org}/issue-fields -H "X-GitHub-Api-Version: 2026-03-10"
读取 issue field 值gh api /repos/{owner}/{repo}/issues/{number}/issue-field-values -H "X-GitHub-Api-Version: 2026-03-10"
写入 issue field 值gh api /repositories/{repo_id}/issues/{number}/issue-field-values -X POST -H "X-GitHub-Api-Version: 2026-03-10" --input -
获取仓库 IDgh api /repos/{owner}/{repo} --jq .id
列出仓库标签gh label list -R {owner}/{repo} --limit 1000 --json name,color,description
按标签列出 issuegh issue list -R {owner}/{repo} --label "{name}" --state all --json number,title,labels --limit 1000
从 issue 移除标签gh api /repos/{owner}/{repo}/issues/{number}/labels/{label_name} -X DELETE

完整 API 详情请参阅 references/issue-fields-api.md、references/projects-api.md 和 references/labels-api.md。

工作流

步骤 0:迁移来源

询问用户要迁移什么:

  1. “您要迁移标签还是项目字段?”

  2. 如果用户回答标签:

    • 询问:“这些标签在哪个组织和哪些仓库中?”
    • 询问:“您想迁移哪些标签?”(他们可以指定名称,或说“先给我看看有哪些标签”)
  3. 如果用户回答项目字段:

    • 询问:“您能分享项目的链接,或者告诉我组织名称和项目编号吗?”
    • 询问:“您想迁移哪个字段?”

标签迁移流程

当用户希望将仓库标签转换为议题字段值时,使用此流程。标签只能映射到 single_select 议题字段(每个标签名称映射到一个选项值)。

阶段 L1:输入与标签发现
  1. 询问用户:要迁移的组织名称和仓库。
  2. 从每个仓库获取标签:
gh label list -R {owner}/{repo} --limit 1000 --json name,color,description
  1. 获取组织议题字段:
gh api /orgs/{org}/issue-fields \
  -H "X-GitHub-Api-Version: 2026-03-10" \
  --jq '.[] | {id, name, content_type, options: [.options[]?.name]}'
  1. 筛选(针对标签数量较多的仓库):如果仓库有 50+ 个标签,按常见前缀(例如 priority-*、team-*、type-*)或颜色分组。让用户通过“显示匹配 priority 的标签”或“显示蓝色标签”来筛选,然后再进行映射。切勿一次性列出 100+ 个标签。

  2. 询问用户哪些标签映射到哪个议题字段和选项。支持以下模式:

    • 单个标签映射到单个字段:例如,标签 "bug" → Type 字段,"Bug" 选项
    • 多个标签映射到一个字段(批量):例如,标签 p0、p1、p2、p3 → Priority 字段及匹配的选项
    • 多个标签映射到多个字段:例如,p1 → Priority + frontend → Team。作为单独的映射组处理。
  3. 自动建议映射:对于每个标签,尝试使用以下模式(按顺序)匹配议题字段选项:

    • 精确匹配(不区分大小写):标签 Bug → 选项 Bug
    • 前缀-数字({prefix}-{n} → {P}{n}):标签 priority-1 → 选项 P1
    • 去除分隔符(连字符、下划线、空格):标签 good_first_issue → 选项 Good First Issue
    • 子字符串包含:标签 type: bug → 选项 Bug

    一次性呈现所有建议,供用户确认、更正或跳过。

示例输出:

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?
阶段 L2:冲突检测

在确定标签到选项的映射后,检查冲突。当一个议题有多个标签映射到同一个议题字段时,就会发生冲突(因为 single_select 字段只能保存一个值)。

  1. 按目标议题字段对标签映射进行分组。
  2. 对于有多个标签来源的每个字段,注意潜在的冲突。
  3. 询问用户冲突解决策略:
    • 首次匹配:使用找到的第一个匹配标签(按标签映射列表的顺序)
    • 跳过:跳过具有冲突标签的议题并报告它们
    • 手动:逐个呈现每个冲突,由用户决定

示例:

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
阶段 L3:预检与数据扫描
  1. 对于每个仓库,验证写入权限并缓存 repository_id:
gh api /repos/{owner}/{repo} --jq '{full_name, id, permissions: .permissions}'
  1. 对于映射中的每个标签,获取匹配的议题:
gh issue list -R {owner}/{repo} --label "{label_name}" --state all \
  --json number,title,labels,type --limit 1000

警告:--limit 1000 会静默截断结果。如果你预计某个标签可能有超过 1000 个议题,请手动分页或先验证总数(例如,gh issue list --label "X" --state all --json number | jq length)。

PR 筛选:gh issue list 同时返回议题和 PR。在 --json 输出中包含 type,如果用户只想迁移议题,则筛选 type == "Issue"。

  1. 如果所有选定的标签都返回 0 个议题,停止并告知用户。建议:尝试不同的标签、检查拼写或尝试不同的仓库。不要继续进行空迁移。

  2. 对于多仓库迁移,在所有指定的仓库中重复上述步骤。

  3. 对于找到的每个议题:

    • 检查议题是否已有目标议题字段的值(如果已设置则跳过)。
    • 检测多标签冲突(议题有两个标签映射到同一字段)。
    • 应用阶段 L2 中选择的冲突解决策略。
    • 分类:迁移、跳过(已设置)、跳过(冲突)或跳过(无匹配标签)。
阶段 L4:预览 / 试运行

在任何写入之前呈现摘要。

示例预览:

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?
阶段 L5:执行
  1. 对于每个要迁移的 issue,写入 issue 字段值(与 project 字段迁移使用相同的端点):
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 -

将 FIELD_ID 替换为整数字段 ID(例如 1),将 OPTION_NAME 替换为选项名称字符串。

  1. 如果用户选择移除标签,则在字段写入成功后,移除每个已迁移的标签:
gh api /repos/{owner}/{repo}/issues/{number}/labels/{label_name} -X DELETE

对包含空格或特殊字符的标签名进行 URL 编码。

  1. 节奏控制:调用之间延迟 100ms。遇到 HTTP 429 时使用指数退避(1s、2s、4s,最长 30s)。
  2. 进度:每 25 项报告一次(例如 "Migrated 75/156 issues...")。
  3. 错误处理:记录失败但继续执行。将标签移除失败单独记录。
  4. 最终汇总:
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)

Project 字段迁移流程

当用户希望将 GitHub Project V2 字段中的值复制到对应的组织级 issue 字段时,使用此流程。

按顺序执行以下六个阶段。执行前始终先预览。

阶段 P1:输入与发现
  1. 向用户询问:组织名称和 project 编号(或 project URL)。
  2. 获取 project 字段:
# Use MCP tool
mcp__github__projects_list(owner: "{org}", project_number: {n}, method: "list_project_fields")
  1. 获取组织 issue 字段:
gh api /orgs/{org}/issue-fields \
  -H "X-GitHub-Api-Version: 2026-03-10" \
  --jq '.[] | {id, name, content_type, options: [.options[]?.name]}'
  1. 过滤代理字段:在 project 上启用 issue 字段后,某些 project 字段会以 "proxy" 条目形式出现,其单选类型的 options: [] 为空。这些条目镜像了真实的 issue 字段,应被忽略。仅与具有实际选项值的 project 字段进行匹配。

  2. 按名称(不区分大小写)自动匹配类型兼容的字段:

Project 字段类型Issue 字段类型是否兼容?
TEXTtext是,直接复制
SINGLE_SELECTsingle_select是,需要选项映射
NUMBERnumber是,直接复制
DATEdate是,直接复制
ITERATION(无)无对应类型;跳过并发出警告
  1. 以表格形式展示建议的字段映射。让用户确认、调整或跳过字段。

示例输出:

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.
阶段 P2:选项映射(仅限单选字段)

对于每一对匹配的单选字段:

  1. 比较 project 字段与 issue 字段之间的选项名称(不区分大小写)。
  2. 自动匹配名称相同的选项。
  3. 对于任何未映射的 project 字段选项,在单个汇总中展示所有未映射的选项,并要求用户一次性为所有选项提供映射。不要逐条提示;将它们合并为一次交互。
  4. 展示最终选项映射表以供确认。

示例输出:

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").
阶段 P3:飞行前检查

在扫描条目之前,验证对每个可能被触及的仓库的写入权限:

  1. 从 project 条目(第一页)中,收集 {owner}/{repo} 值的唯一集合。
  2. 对于每个唯一的仓库,验证已认证用户具有 Issues 写入权限:
gh api /repos/{owner}/{repo} --jq '{full_name, permissions: .permissions}'
  1. 如果任何仓库显示 push: false 或 triage: false,则在继续之前警告用户。这些仓库中的条目将在写入时失败。
  2. 现在缓存每个仓库的 repository_id(整数);你在阶段 P6 中会需要它:
gh api /repos/{owner}/{repo} --jq .id
阶段 P4:数据扫描
  1. 使用 MCP 获取所有项目条目。重要:对于条目超过约 200 个的项目,gh api graphql --paginate 不可靠(它会拼接 JSON 响应而不添加适当的分隔符,并且可能超时)。请使用在内部处理分页的 MCP 工具,或使用显式的基于游标的分页:
# Preferred: use MCP tool (handles pagination automatically)
mcp__github__projects_list(owner: "{org}", project_number: {n}, method: "list_project_items")

# Fallback for large projects: manual cursor-based pagination
# 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.
  1. 对于每个条目:
    • 如果是草稿条目(不是真实 issue),则跳过。
    • 提取源项目字段的值。
    • 如果源值为空,则跳过。
    • 检查该 issue 是否已为目标 issue 字段设置了值:
gh api /repos/{owner}/{repo}/issues/{number}/issue-field-values \
  -H "X-GitHub-Api-Version: 2026-03-10"
  • 如果该 issue 字段已有值,则跳过(保留现有数据)。
  1. 将每个条目归入以下类别之一:
    • 迁移:有源值,且没有现有的目标值
    • 跳过(已设置):目标 issue 字段已有值
    • 跳过(无源值):该条目的项目字段为空
    • 跳过(草稿):条目是草稿,不是真实 issue
    • 跳过(未映射选项):单选值未映射
阶段 P5:预览 / 试运行

在任何写入之前先呈现摘要。

如果用户请求试运行:显示完整的详细报告(每个 issue、其当前值、建议的新值以及跳过原因)并停止。不要执行。

否则(预览模式):显示汇总计数和变更示例,然后请求确认。

示例预览:

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.
阶段 P6:执行
  1. 使用在阶段 3 中缓存的 repository_id 值。

  2. 对于每个要迁移的条目,写入 issue 字段值:

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(例如 1),将 VALUE 替换为值字符串。

  1. 节奏控制:在 API 调用之间添加 100ms 延迟。对于 HTTP 429 响应,使用指数退避(1s、2s、4s,最多 30s)。
  2. 进度:每 25 个条目报告一次状态(例如“Migrated 75/847 items...”)。
  3. 错误处理:记录失败,但继续处理剩余条目。
  4. 最终摘要:
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)
  ...

重要说明

  • 写入端点的小陷阱:用于写入 issue 字段值的 REST API 使用 repository_id(整数),而不是 owner/repo。务必先用 gh api /repos/{owner}/{repo} --jq .id 查询仓库 ID。
  • 单选值:REST API 接受选项名称字符串(而非选项 ID)。这使得项目字段和标签的映射都变得直接明了。
  • 读回值:从 API 响应中读取 issue 字段值时,使用 .single_select_option.name 获取人类可读的值。.value 属性返回的是内部选项 ID(例如 1201 这样的整数),而不是显示名称。
  • API 版本标头:所有 issue 字段端点都需要 X-GitHub-Api-Version: 2026-03-10。
  • 跨仓库条目:一个项目可以包含来自多个仓库的 issue。按仓库缓存仓库 ID,以避免重复查询。
  • 保留现有值:绝不覆盖已设置的 issue 字段值。跳过这些条目。
  • 迭代字段:没有对应的 issue 字段。始终警告用户并跳过。
  • 草稿条目:未关联真实 issue 的项目条目不能有 issue 字段值。跳过并注明。
  • 标签是仓库作用域的:与项目字段不同,标签是按仓库存在的。同一标签名可能存在于多个仓库中;迁移需分别对每个仓库执行。
  • 标签冲突:一个 issue 可能有多个标签映射到同一个 single_select 字段。务必在执行前检测并解决这些冲突。
  • 标签移除是可选的:迁移后,用户可能希望保留标签作为备份,或将其移除。移除前务必询问。
  • 对标签名进行 URL 编码:含有空格或特殊字符的标签在用于 REST API 路径时必须进行 URL 编码(例如 good%20first%20issue)。
  • 为规模化生成脚本:对于 100+ 个 issue 的迁移,生成独立的 shell 脚本,而不是通过 Agent 逐个执行 API 调用。这样更快、可恢复,并避免 Agent 超时问题。
  • 幂等迁移:重新运行迁移是安全的。已设置目标字段值的 issue 将被跳过。这意味着你可以安全地恢复部分完成的迁移,而不会重复工作。
  • --limit 1000 截断:gh issue list --limit 1000 会在 1000 条结果处静默停止。对于 issue 数量更多的标签,使用 --jq 和基于游标的分页进行分页,或运行多个带过滤条件的查询(例如按日期范围)。
  • macOS bash 版本:macOS 自带 bash 3.x,不支持 declare -A(关联数组)。生成的脚本应使用 POSIX 兼容的构造,或注明不兼容并建议 brew install bash。
  • Issue 与 PR 的区别:gh issue list 同时返回 issue 和拉取请求。如果迁移只应针对 issue,请在 --json 输出中包含 type,并过滤 type == "Issue"。

示例

示例 1:完整迁移

用户:"我需要把项目中的 Priority 值迁移到新的组织级 Priority issue 字段"

操作:遵循阶段 P1-P6。发现字段、映射选项、检查权限、扫描条目、预览、执行。

示例 2:仅试运行

用户:"给我看看如果从项目 #42 迁移字段会发生什么,但不要真的执行"

操作:仅遵循阶段 P1-P5。呈现包含每个条目在内的完整试运行报告。不要执行。

示例 3:多个字段

用户:"把项目 #15 的 Priority 和 Due Date 迁移到 issue 字段"

操作:相同的工作流,但在一次处理中同时处理两个字段。在数据扫描期间,为每个条目收集所有已映射字段的值。对每个 issue 在一次 API 调用中写入所有字段值。

示例 4:将单个标签迁移到 Issue 字段

用户:"我想把 'bug' 标签迁移到 Type issue 字段"

操作:路由到 Label Migration Flow。询问 org/repo,列出标签,确认映射:标签 "bug" → Type 字段的 "Bug" 选项。扫描带有该标签的 issue,预览,执行。询问迁移后是否删除该标签。

示例 5:将多个标签迁移到一个字段(批量)

用户:"我们有 p0、p1、p2、p3 标签,想把它们转换为 Priority issue 字段"

操作:路由到 Label Migration Flow。将全部四个标签映射到 Priority 字段选项(p0→P0、p1→P1、p2→P2、p3→P3)。检查冲突(同时带有多个优先级标签的 issue)。在一个汇总中预览所有更改。一次性执行。可选地从已迁移的 issue 中删除全部四个标签。

示例 6:跨仓库标签迁移并删除标签

用户:"把 'frontend' 和 'backend' 标签迁移到 Team issue 字段,涉及 github/issues、github/memex 和 github/mobile,然后删除旧标签"

操作:路由到 Label Migration Flow。确认仓库和标签映射:"frontend"→Team "Frontend","backend"→Team "Backend"。扫描全部三个仓库中带有这些标签的 issue。检测冲突(同时带有两个标签的 issue)。跨仓库预览。执行字段写入,然后从已迁移的 issue 中删除标签。按仓库报告统计数据。

评论

支持 Markdown