Back to skills
SKILL.md
Gh Labels Milestones
ASecurityBootstrap and sync GitHub labels and milestones idempotently. Use when setting up a new repository or syncing label definitions.
- 24 stars
- 0 votes
- 0 copies
- 4 views
- Added September 10, 2026
Works with
Security analysis
100/100npx -y skills add kinncj/MAPLE --skill gh-labels-milestones --agent claude-codeAre you the author of Gh Labels Milestones?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/kinncj-gh-labels-milestones-6e81cc8c)---
name: gh-labels-milestones
description: "Bootstrap and sync GitHub labels and milestones idempotently. Use when setting up a new repository or syncing label definitions."
---
# SKILL: gh-labels-milestones
## Purpose
Bootstrap and sync GitHub labels and milestones idempotently. Labels and milestones are created if absent; existing ones are updated if color or description differs. Never delete — only add or update.
## Label Bootstrap
Use this at project start (`maple labels`) and in CI to guarantee label state.
```bash
# Idempotent label create-or-update
upsert_label() {
local name="$1" color="$2" description="$3"
if gh label list --json name --jq '.[].name' | grep -qx "$name"; then
gh label edit "$name" --color "$color" --description "$description"
else
gh label create "$name" --color "$color" --description "$description"
fi
}
```
## Label Groups
### Phase Labels (pipeline position)
```bash
upsert_label "phase:discover" "0075ca" "Phase 1: Discovery"
upsert_label "phase:architect" "0075ca" "Phase 2: Architecture"
upsert_label "phase:plan" "0075ca" "Phase 3: Planning"
upsert_label "phase:infra" "0075ca" "Phase 4: Infrastructure"
upsert_label "phase:implement" "0075ca" "Phase 5: Implementation"
upsert_label "phase:validate" "0075ca" "Phase 6: Validation"
upsert_label "phase:document" "0075ca" "Phase 7: Documentation"
upsert_label "phase:done" "0075ca" "Phase 8: Complete"
```
### Type Labels (work classification)
```bash
upsert_label "type:feature" "0052cc" "New capability"
upsert_label "type:bug" "d73a4a" "Defect"
upsert_label "type:spike" "e4e669" "Research / time-boxed exploration"
upsert_label "type:chore" "ededed" "Non-functional maintenance"
upsert_label "type:refactor" "ededed" "Code restructuring, no behavior change"
upsert_label "type:docs" "0075ca" "Documentation only"
```
### Priority Labels (MoSCoW)
```bash
upsert_label "priority:critical" "b60205" "Must ship — blocks launch"
upsert_label "priority:high" "e11d48" "Must have"
upsert_label "priority:medium" "f97316" "Should have"
upsert_label "priority:low" "84cc16" "Could have"
upsert_label "priority:wontfix" "ffffff" "Won't have this cycle"
```
### Spec / Story Kit Labels
```bash
upsert_label "spec:problem" "7057ff" "Problem statement written"
upsert_label "spec:approved" "7057ff" "Spec approved by PO"
upsert_label "spec:in-review" "7057ff" "Spec under review"
```
### Design Labels
```bash
upsert_label "design:pending" "fbca04" "Awaiting design"
upsert_label "design:in-progress" "fbca04" "Design work active"
upsert_label "design:approved" "fbca04" "Design approved"
upsert_label "design:a11y-passed" "fbca04" "Accessibility review passed"
```
### ADR Labels
```bash
upsert_label "adr:required" "5319e7" "ADR must be written before implementation"
upsert_label "adr:in-progress" "5319e7" "ADR being authored"
upsert_label "adr:complete" "5319e7" "ADR accepted"
upsert_label "adr:rejected" "5319e7" "ADR rejected — see comments"
```
### UI / Accessibility Labels
```bash
upsert_label "ui:required" "fef2c0" "Has UI surface — design review required"
upsert_label "ui:in-progress" "fef2c0" "UI implementation active"
upsert_label "ui:complete" "fef2c0" "UI complete and reviewed"
```
### Status Labels
```bash
upsert_label "in-progress" "0052cc" "Work started"
upsert_label "blocked" "b60205" "Blocked — needs human"
upsert_label "validated" "0e8a16" "All tests pass"
upsert_label "tdd:red" "d73a4a" "Failing test written"
upsert_label "tdd:green" "0e8a16" "Tests passing"
```
## Milestone Bootstrap
```bash
# Idempotent milestone create
upsert_milestone() {
local title="$1" due="$2" description="$3"
EXISTING=$(gh api "repos/{owner}/{repo}/milestones" \
--jq ".[] | select(.title == \"$title\") | .number" 2>/dev/null)
if [ -n "$EXISTING" ]; then
gh api "repos/{owner}/{repo}/milestones/$EXISTING" \
-X PATCH \
-f title="$title" \
-f due_on="${due}T00:00:00Z" \
-f description="$description"
else
gh api "repos/{owner}/{repo}/milestones" \
-X POST \
-f title="$title" \
-f due_on="${due}T00:00:00Z" \
-f description="$description"
fi
}
# Example usage
upsert_milestone "v1.0" "2025-12-31" "Initial release"
```
## Milestone granularity gate (ask once, then persist)
Whether a change gets a milestone is driven by `github.milestone_granularity` in
`project.config.yaml`. An absent key in an older config counts as `null`.
```bash
GRANULARITY=$(python3 -c "
for line in open('project.config.yaml'):
s = line.strip()
if s.startswith('milestone_granularity:'):
v = s.split(':', 1)[1].split('#', 1)[0].strip().strip('\"').strip(\"'\")
print(v); break
")
```
- **`null` or empty** — not yet decided. Ask the user **once**:
"Track milestones for this project? (yes = major & minor releases / no = skip)".
Persist the answer so we never re-ask. The key is **absent** in older configs,
so replace it if present, otherwise insert it under the `github:` block:
```bash
python3 - "$ANSWER" <<'PY' # ANSWER = "minor" (yes) or "none" (no)
import re, sys
val = sys.argv[1]
p = 'project.config.yaml'; s = open(p).read()
if re.search(r'^\s*milestone_granularity:', s, re.M):
s = re.sub(r'(^\s*milestone_granularity:\s*)\S+', r'\g<1>' + val, s, count=1, flags=re.M)
elif re.search(r'^github:\s*$', s, re.M):
s = re.sub(r'(^github:\s*\n)', r'\g<1> milestone_granularity: ' + val + '\n', s, count=1, flags=re.M)
else:
s = s.rstrip('\n') + '\ngithub:\n milestone_granularity: ' + val + '\n'
open(p, 'w').write(s)
PY
```
- **`none`** — declined. Skip milestones entirely.
- **`minor`** (default when enabled) — **major & minor only**. Ensure `vX.Y.0`
exists (`upsert_milestone`); a **patch** attaches to its minor's `vX.Y.0` and
never gets its own milestone.
- **`patch`** — also give patches their own `vX.Y.Z` milestone.
The label taxonomy (`type:bug` / `type:feature` / `type:docs` / `type:refactor` /
`type:chore`) is the single source of truth defined above under **Type Labels** —
map the SemVer class to it (`type:bug` for a fix/patch, `type:feature` for a
feature/minor/major). Do not invent `bug`/`enhancement` labels.
## Assign Labels to an Issue
```bash
# Add labels (idempotent — gh ignores if already present)
gh issue edit {issue_number} \
--add-label "type:feature,priority:high,phase:discover"
# Remove a specific label
gh issue edit {issue_number} --remove-label "phase:discover"
```
## Failure Modes
| Condition | Action |
|---|---|
| Label name collision (wrong color) | `gh label edit` — update in place |
| Milestone already exists | Patch via API — do not create duplicate |
| `gh` not authenticated | Stop immediately. Do not retry. |
| Label with special characters | URL-encode the label name in API calls |
## Logging
```
[gh-labels] UPSERT label="type:feature" color=0052cc
[gh-labels] SKIP label="priority:high" (unchanged)
[gh-milestones] CREATE "v1.0" due=2025-12-31
[gh-milestones] SKIP "v1.0" already exists
```
Attribution
Comments
Loading comments…
Newsletter
Get the weekly list of safe new skills, and the ones we pulled.