setup-github-labels

star 34

Applies one small canonical GitHub label set to the current repo, so every repo you run it in speaks the same label vocabulary: an issue `type:` axis (bug/feature/task, mirroring GitHub Issue Types), the five whose-turn triage roles (needs-triage/needs-info/ready-for-agent/ready-for-human/wontfix), and a PR-verdict axis (recommend-merge/recommend-close/recommend-triage). This skill owns the GitHub label objects (name, color, description); `setup-matt-pocock-skills` owns the triage roles' agent wiring. Use when setting up labels on a repo, or after editing this skill's canonical tables. Invoke explicitly; it is not auto-triggered.

wkentaro By wkentaro schedule Updated 6/14/2026

name: setup-github-labels description: "Applies one small canonical GitHub label set to the current repo, so every repo you run it in speaks the same label vocabulary: an issue type: axis (bug/feature/task, mirroring GitHub Issue Types), the five whose-turn triage roles (needs-triage/needs-info/ready-for-agent/ready-for-human/wontfix), and a PR-verdict axis (recommend-merge/recommend-close/recommend-triage). This skill owns the GitHub label objects (name, color, description); setup-matt-pocock-skills owns the triage roles' agent wiring. Use when setting up labels on a repo, or after editing this skill's canonical tables. Invoke explicitly; it is not auto-triggered."

Setup GitHub Labels

Apply one intentional label vocabulary to whatever repo you run this in. The tables below are the source of truth, not the GitHub UI. This is a prompt-driven skill, not a deterministic script: read the set, explore the repo, confirm with the user, then create the labels with gh.

The canonical set

Three groups, kept small on purpose. This skill owns the GitHub label objects (name, color, description) for all of them. setup-matt-pocock-skills owns the agent wiring for the triage roles — the role→string mapping in docs/agents/triage-labels.md that tells the triage skill which string to apply — and never touches a label's color or description. So the two skills split by concern, not by which labels: one owns the labels on GitHub, the other owns how the agent refers to them. Anything a tool already owns (Dependabot, labeler actions) is not here.

Issue type: axis

Label Color Description (issue-scoped)
type: bug d73a4a issue: Reporting a defect to fix
type: feature a2eeef issue: Requesting a new capability or improvement
type: task cfd3d7 issue: For other work: maintenance, refactor, docs

type: bug / type: feature / type: task mirror GitHub's Issue Types (Bug / Feature / Task). task is the catch-all for refactor/docs/chore/test work. If a repo ever moves under a GitHub org, these map 1:1 onto native Issue Types and the labels can be retired.

Issue triage axis

The five whose-turn triage roles the triage skill moves an issue through. setup-matt-pocock-skills decides which string each role maps to (and records it in docs/agents/triage-labels.md); this skill creates the actual GitHub labels with a consistent scope prefix, so it reads at a glance whether a state applies to issues, PRs, or both.

Label Color Description
needs-triage fbca04 issue: Maintainer needs to evaluate this issue
needs-info d876e3 issue/pr: Waiting on reporter for more information
ready-for-agent 0e8a16 issue: Fully specified, ready for an AFK agent
ready-for-human 1d76db issue: Requires human implementation
wontfix ffffff issue: Will not be actioned

needs-info is the one issue/pr: label: a PR parked on an outside human reuses it rather than minting a PR-scoped twin, so "waiting on a human" reads identically on both. Every other triage role is issue-only.

PR-verdict axis

Whose-turn routing on a PR needs almost no labels: a non-draft PR with no verdict is, by definition, the agent's to finalize, and a PR parked on an outside human reuses the needs-info triage label above rather than minting a PR-scoped twin. What this axis adds is the agent's terminal verdict once it has finished finalizing a PR: the signal that tells the maintainer, at a glance, what each open PR needs from them.

Label Color Description (PR-scoped)
recommend-merge 0E8A16 pr: Agent finalized and endorses it: review and merge
recommend-close D93F0B pr: Agent recommends closing: your call to review or close
recommend-triage FBCA04 pr: Agent finalized it but the merge/close call is yours

The verdict is three-way by design: the agent finalizes a PR (rebase, green CI, polish) and then emits exactly one of the three. The split exists because "not mergeable as-is" hides two states with very different maintainer effort:

  • recommend-close is the agent's active reject — broken, abandoned, superseded, or clearly out of scope. The maintainer glances and closes.
  • recommend-triage is for a PR whose code is sound but whose merge/close call is a product or scope judgment the agent can't make — a clean, working feature where the only open question is "does this project want it". The maintainer must stop and decide; that decision is theirs, not the agent's.

Folding the second into recommend-close would be dishonest — the agent does not recommend closing a sound feature — and would bury the most expensive pile (the product calls only the maintainer can make) inside the cheap one (rubber-stamp closes). So the maintainer's world is three filters: an emerald ship queue (is:pr is:open label:recommend-merge), an amber decision queue (label:recommend-triage), and a red close pile (label:recommend-close).

recommend-triage is not a cop-out hatch. It is only for "code is sound, the call is product/scope". If the agent has a technical reason the PR shouldn't merge, it still owes a recommend-close with that reason in the review comment — otherwise the decision queue swells with PRs the agent could have resolved and the label loses its meaning.

The agent never merges and never closes — both stay the maintainer's hand; the verdict is a recommendation, not an action. That is why all three labels lead with recommend- rather than the ecosystem-conventional ready-to-merge: the matched recommend-merge / recommend-close / recommend-triage set names them honestly as recommendations, and — deliberately — steers clear of the label strings merge bots watch (Kodiak, Mergify, bors, GitHub auto-merge). A bot wired to merge on ready-to-merge would turn the agent's recommendation into an actual merge and break this invariant, so do not rename it back to that conventional string.

A verdict endorses one specific diff, so it goes stale the moment the PR changes: a new commit after a verdict means the agent must clear that label and re-review before the emerald queue can be trusted again. Pushes after a verdict are rare, so this stays a manual step rather than something worth a CI workflow.

A couple of label families are deliberately left out, because a tool already owns them:

  • Tool-managed labels (dependencies from Dependabot, and other labels created by labeler actions or bots) are owned by that tooling. Leave their color and description alone; do not add them here or you will fight the tool that recreates them.
  • area:* labels are per-repo (add them locally, ideally via path-based actions/labeler), so they do not belong in a shared cross-repo set.

The "still being worked" state is owned by GitHub's native draft flag, not a label. A PR mid-iteration (including agent- or self-authored PRs, where the author is the maintainer so review-request doesn't apply) stays a draft until the agent has a verdict. So there is deliberately no ready-for-maintainer / ready-for-review label: recommend-merge already is the "your turn to ship" signal, and the draft flag already owns "not yet". The maintainer's "needs my attention" filter is is:pr is:open label:recommend-merge. /make-pr opens autonomous PRs as draft for exactly this reason.

The same draft flag is why there is no do-not-merge label: a "don't merge this yet" state is almost always temporary (a PR kept open to exercise CI, or one mid-iteration), and that is precisely what draft expresses. A draft PR already cannot be merged, so a separate flag would just be a second, redundant mechanism for "not yet".

PR type and area stay absent: PR type comes from the conventional-commit title, and area is per-repo. Only the PR verdict is shared here, because "is this one mine to ship" generalizes across every repo with PRs. Each description leads with a scope prefix — issue:, pr:, or issue/pr: — to advertise where it applies, so tools and people don't cross-apply.

Issue ↔ PR equivalents

Issues and PRs run the same underlying state machine — whose turn is it, and what must they do — but express it with different signals. This skill creates both sides' labels; setup-matt-pocock-skills owns the issue side's vocabulary (which string each triage role maps to). The PR side also leans on GitHub's native draft flag. This table lines them up so a state reads the same whether you're looking at an issue or a PR:

State (whose turn / what's needed) Issue (triage role) PR (verdict + draft flag)
Unprocessed — someone must look needs-triage no verdict label, non-draft
Blocked on an outside human needs-info needs-info (same label, reused)
Still being built / iterated (open issue, no extra label) draft flag
Agent's turn to act ready-for-agent no verdict label, non-draft
Maintainer's turn — endorsed ready-for-human (human implements) recommend-merge (human reviews/merges)
Maintainer's turn — must decide needs-triage (maintainer evaluates) recommend-triage (product/scope call)
Won't proceed wontfix recommend-close

Three asymmetries are intentional, not gaps:

  • needs-triage and ready-for-agent collapse into one PR state. A non-draft PR with no verdict already means "agent, finalize this", so the PR side never separates "needs evaluation" from "agent's turn" — there is no PR needs-triage or ready-for-agent label. On the issue side they stay distinct because an issue can sit triaged-but-not-yet-assigned.
  • The maintainer's terminal action differs. ready-for-human on an issue means implement it; recommend-merge on a PR means review and merge it. Same "your turn, human" role, different verb — which is why ready-for-human stays issue-only and recommend-merge is its PR counterpart rather than a shared label.
  • needs-triage spans two PR states. On issues, "maintainer must evaluate" is one state. On PRs it splits by when: a fresh PR is the agent's to finalize (no verdict, non-draft), and only after the agent finalizes does a leftover product/scope call earn its own verdict, recommend-triage. That is why recommend-triage is a distinct PR label and the agent never puts needs-triage on a PR (that would invert it to "agent hasn't looked yet").

do-not-merge is absent on purpose: a "don't merge yet" PR is the draft row above, so it needs no label of its own.

Process

Detect, preview, confirm, then apply.

1. Detect the current repo

gh repo view --json nameWithOwner -q .nameWithOwner

If that fails (no GitHub remote), ask the user which repo to target.

2. Preview (read-only)

Show the user which canonical labels are new vs already present on the repo, by comparing the tables' labels against the repo's existing ones:

gh label list --repo "$REPO" --limit 200 --json name -q '.[].name'

Labels in the tables but not in that list will be created; labels already present will have their color/description updated.

3. Confirm, then apply

Creating labels on a (often public) repo is an outward-facing action, so confirm with the user first. Then create each label from the tables with --force, which adds it if missing and updates color/description if it already exists:

gh label create "type: bug"     --color d73a4a --description "issue: Reporting a defect to fix"                   --force --repo "$REPO"
gh label create "type: feature" --color a2eeef --description "issue: Requesting a new capability or improvement"  --force --repo "$REPO"
gh label create "type: task"    --color cfd3d7 --description "issue: For other work: maintenance, refactor, docs" --force --repo "$REPO"
gh label create "needs-triage"    --color fbca04 --description "issue: Maintainer needs to evaluate this issue"     --force --repo "$REPO"
gh label create "needs-info"      --color d876e3 --description "issue/pr: Waiting on reporter for more information" --force --repo "$REPO"
gh label create "ready-for-agent" --color 0e8a16 --description "issue: Fully specified, ready for an AFK agent"     --force --repo "$REPO"
gh label create "ready-for-human" --color 1d76db --description "issue: Requires human implementation"              --force --repo "$REPO"
gh label create "wontfix"         --color ffffff --description "issue: Will not be actioned"                       --force --repo "$REPO"
gh label create "recommend-merge"  --color 0E8A16 --description "pr: Agent finalized and endorses it: review and merge"   --force --repo "$REPO"
gh label create "recommend-close"  --color D93F0B --description "pr: Agent recommends closing: your call to review or close" --force --repo "$REPO"
gh label create "recommend-triage" --color FBCA04 --description "pr: Agent finalized it but the merge/close call is yours" --force --repo "$REPO"

To set up several repos, repeat with each --repo.

4. Report

Tell the user which labels were created vs updated. Non-canonical labels already on the repo are left untouched (this skill only adds/updates the canonical set). If they want to retire a stray label, that is a manual, deliberate step: gh label delete "<name>" --repo "$REPO" --yes. Deleting strips it off every issue/PR currently wearing it, so never do it without explicit confirmation.

This skill creates the triage labels, but the engineering skills only apply them if the repo also has the agent wiring — the role→string mapping in docs/agents/triage-labels.md. If that file is absent (test -f docs/agents/triage-labels.md), recommend running setup-matt-pocock-skills to write it. That's a one-line pointer, not a requirement: the labels stand on their own; the wiring is what lets the triage skill reach for them. Skip the recommendation when the file is already present.

Changing the set

Edit the tables above: add or remove a row, and keep the gh label create lines in step 3 in sync with them. For a triage role, also keep it consistent with the role→string mapping setup-matt-pocock-skills records in docs/agents/triage-labels.md — this skill owns the label's color and description, that skill owns which string the role maps to. Keep the set small: before adding a label, check it carries information the commit title, diff, or an existing label (or another skill's labels) doesn't.

Install via CLI
npx skills add https://github.com/wkentaro/dotfiles --skill setup-github-labels
Repository Details
star Stars 34
call_split Forks 6
navigation Branch main
article Path SKILL.md
More from Creator