npx skills add ...
npx skills add github/awesome-copilot --skill issue-fields-migration
Bulk-migrate metadata to GitHub issue fields from two sources: repo labels (e.g. priority labels to a Priority field) and Project V2 fields. Use when users say "migrate my labels to issue fields", "migrate project fields to issue fields", "convert labels to issue fields", "copy project field values to issue fields", or ask about adopting issue fields. Issue fields are org-level typed metadata (single select, text, number, date) that replace label-based workarounds with structured, searchable, cross-repo fields.
npx skills add github/awesome-copilot --skill issue-fields-migration
Issue fields are org-level typed metadata (single select, text, number, date) that replace label-based workarounds with structured, searchable, cross-repo fields. Every organization gets Priority, Effort, Start date, and Target date preconfigured, with support for up to 25 custom fields.
This skill bulk-migrates existing metadata into issue fields from two sources:
p0, p1, priority/high into structured issue field values (e.g. the Priority field). Supports migrating multiple labels at once and optionally removing them after migration.gh CLI must be authenticated with appropriate scopes| Tool | Purpose |
|---|---|
mcp__github__projects_list | List project fields (list_project_fields), list project items with values (list_project_items) |
mcp__github__projects_get | Get details of a specific project field or item |
| Operation | Command |
|---|---|
| List org issue fields | gh api /orgs/{org}/issue-fields -H "X-GitHub-Api-Version: 2026-03-10" |
| Read issue field values | gh api /repos/{owner}/{repo}/issues/{number}/issue-field-values -H "X-GitHub-Api-Version: 2026-03-10" |
| Write issue field values | gh api /repositories/{repo_id}/issues/{number}/issue-field-values -X POST -H "X-GitHub-Api-Version: 2026-03-10" --input - |
| Get repository ID | gh api /repos/{owner}/{repo} --jq .id |
| List repo labels | gh label list -R {owner}/{repo} --limit 1000 --json name,color,description |
| List issues by label | gh issue list -R {owner}/{repo} --label "{name}" --state all --json number,title,labels --limit 1000 |
| Remove label from issue | gh api /repos/{owner}/{repo}/issues/{number}/labels/{label_name} -X DELETE |
See references/issue-fields-api.md, references/projects-api.md, and references/labels-api.md for full API details.
Ask the user what they are migrating:
"Are you migrating labels or project fields?"
If the user says labels:
If the user says project fields:
Use this flow when the user wants to convert repo labels into issue field values. Labels can only map to single_select issue fields (each label name maps to one option value).
Filtering (for repos with many labels): if the repo has 50+ labels, group by common prefix (e.g., priority-*, team-*, type-*) or color. Let the user filter with "show labels matching priority" or "show blue labels" before mapping. Never dump 100+ labels at once.
Ask the user which labels map to which issue field and option. Support these patterns:
Auto-suggest mappings: for each label, attempt to match issue field options using these patterns (in order):
Bug → option Bug{prefix}-{n} → {P}{n}): label priority-1 → option P1good_first_issue → option Good First Issuetype: bug → option BugPresent all suggestions at once for the user to confirm, correct, or skip.
Example output:
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).
Example:
repository_id:Warning: --limit 1000 silently truncates results. If you expect a label may have more than 1000 issues, paginate manually or verify the total count first (e.g., gh issue list --label "X" --state all --json number | jq length).
PR filtering: gh issue list returns both issues and PRs. Include type in the --json output and filter for type == "Issue" if the user only wants issues migrated.
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:
Present a summary before any writes.
Example preview:
Replace FIELD_ID with the integer field ID (e.g., 1) and OPTION_NAME with the option name string.
URL-encode label names that contain spaces or special characters.
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.
Filter out proxy fields: after issue fields are enabled on a project, some project fields appear as "proxy" entries with empty options: [] for single-select types. These mirror the real issue fields and should be ignored. Only match against project fields that have actual option values.
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 |
Example output:
For each matched single-select pair:
Example output:
Before scanning items, verify write access to each repository that may be touched:
{owner}/{repo} values.push: false or triage: false, warn the user before proceeding. Items in those repos will fail at write time.repository_id (integer) for each repo now; you will need it in Phase 6:gh api graphql --paginate 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: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:
Use the repository_id values cached in Phase 3.
For each item to migrate, write the issue field value:
Replace FIELD_ID with the integer field ID (e.g., 1) and VALUE with the value string.
repository_id (integer), not owner/repo. Always look up the repo ID first with gh api /repos/{owner}/{repo} --jq .id..single_select_option.name for the human-readable value. The .value property returns the internal option ID (an integer like 1201), not the display name.X-GitHub-Api-Version: 2026-03-10.good%20first%20issue).--limit 1000 truncation: gh issue list --limit 1000 silently stops at 1000 results. For labels with more issues, paginate with --jq and cursor-based pagination or run multiple filtered queries (e.g., by date range).declare -A (associative arrays). Generated scripts should use POSIX-compatible constructs or note the incompatibility and suggest brew install bash.gh issue list returns both issues and pull requests. If the migration should only target issues, include type in --json output and filter for type == "Issue".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.
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.
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.
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.
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.
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.