Github project board
Skill dustinkeeton/wafflestack/.claude/skills/github-project-board
π§ One batter, every repo β reusable AI agent & skill definitions rendered into harness-native files (.claude/, .codex/, .agents/)
npx -y skills add dustinkeeton/wafflestack --skill github-project-boardAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Create and standardize a GitHub Projects v2 board to the toolkit's canonical Kanban config (Status/Priority/Size/Start/Target fields; Table/Kanban/Roadmap views). Provisions a board when none exists and reconciles missing fields/options on an existing one β always asking before it creates or mutates. Used to bootstrap the board the issue, delegate, and github-project-management skills then consume.
SKILL.md
11.8 KB, as published. Nobody here has run it
GitHub Project Board β provision & standardize
The other board-touching skills only consume an existing Projects v2 board β delegate
syncs Kanban status, issue files new items to "Backlog", and github-project-management
holds the read/update GraphQL catalog. None of them create or standardize a board.
This skill fills that gap: it provisions a board to the toolkit's standard spec when none
exists, and reconciles a partial board up to it β never silently. It always asks before it
creates a board or mutates an existing one.
Use it once, at setup, to bootstrap the board the consumer skills then rely on. For the full
read/update-item query catalog (statuses, dates, milestones, sprint planning) see the
github-project-management skill β this skill covers only the board structure.
Prerequisites
ghCLI authenticated with theprojectscope β board creation and field mutations need it beyond the usualreposcope. If a call fails with a message about theprojectscope, grant it and retry:gh auth refresh -s project- Repo owner / name / node ID:
OWNER=$(gh repo view --json owner -q .owner.login) REPO=$(gh repo view --json name -q .name) REPO_ID=$(gh repo view --json id -q .id) # GraphQL node ID (R_β¦), used to link the board - Owner node ID (works for a user- or org-owned repo):
OWNER_ID=$(gh api graphql -f query=' query($owner: String!) { repositoryOwner(login: $owner) { id } } ' -f owner="$OWNER" --jq '.data.repositoryOwner.id')
The standard board spec
Encoded here as the canonical target (modeled on the "Obsidian Synapse" reference board that
github-project-management's date heuristics already assume). "Standardizing" a board means
reconciling it toward this:
| Field | Type | Options / notes |
|---|---|---|
| Status | single-select (Kanban columns) | Backlog Β· Todo Β· In Progress Β· In Review Β· Done |
| Priority | single-select | Critical Β· High Β· Medium Β· Low |
| Size | single-select | S Β· M Β· L |
| Start | date | Roadmap bar start |
| Target | date | Roadmap bar end |
Views: Table (all items) Β· Kanban (board layout grouped by Status) Β· Roadmap (StartβTarget).
Milestones: used when scoped, from the repo's existing milestone catalog (see
github-project-management). Swimlanes: none by default (the reference board uses none) β
an optional later refinement.
New boards start with only a Status field (GitHub seeds it with Todo / In Progress / Done). Everything else β Backlog & In Review options, Priority, Size, Start, Target β is added by this skill.
Decision flow β ask first, always
- Discover the board matching the project name (below).
- A board already exists β do not silently mutate it. Report what it has vs. the standard spec (missing fields, missing Status options, missing views) and ask the user which to add: e.g. "add the standard config as an additional view/board, and/or add the missing Priority & Size fields and Backlog/In Review Status options?" Apply only what they approve.
- No board exists β ask before creating one. On approval: create it, link it to the repo, then provision the fields/options and set up the views.
Never create or mutate without an explicit go-ahead.
Discover the board (case-insensitive, normalized)
An account may own several projects, and the title may differ from project.name only by case
(WaffleStack vs wafflestack). Match on a normalized, exact title β downcase both sides β
rather than the substring regex the consumer skills use, so a longer project name can't
false-match:
PROJECT_ID=$(gh api graphql -f query='
query($owner: String!) {
user(login: $owner) {
projectsV2(first: 50) { nodes { id number title } }
}
}
' -f owner="$OWNER" \
--jq 'first(.data.user.projectsV2.nodes[]
| select((.title | ascii_downcase) == ("wafflestack" | ascii_downcase))
| .id) // empty')
For an organization-owned repo, replace user(login: $owner) with organization(login: $owner).
PROJECT_ID empty β no matching board (the "no board" path). Non-empty β the "existing board"
path; introspect its fields/options with the Get project fields query in
github-project-management before deciding what is missing.
Create a board (no-board path, after approval)
PROJECT_ID=$(gh api graphql -f query='
mutation($ownerId: ID!, $title: String!) {
createProjectV2(input: {ownerId: $ownerId, title: $title}) {
projectV2 { id number url }
}
}
' -f ownerId="$OWNER_ID" -f title="wafflestack" \
--jq '.data.createProjectV2.projectV2.id')
Link it to the repo so it appears under the repo's Projects tab and the consumer skills find it:
gh api graphql -f query='
mutation($projectId: ID!, $repositoryId: ID!) {
linkProjectV2ToRepository(input: {projectId: $projectId, repositoryId: $repositoryId}) {
repository { id }
}
}
' -f projectId="$PROJECT_ID" -f repositoryId="$REPO_ID"
Provision fields
createProjectV2Field creates a new field. Single-select options are inlined in the
mutation body (not passed as variables β gh api -f only carries scalar variables); each option
needs name, color, description (all required) and color is an unquoted enum from
{GRAY, BLUE, GREEN, YELLOW, ORANGE, RED, PINK, PURPLE}.
Priority (single-select):
gh api graphql -f query='
mutation($projectId: ID!) {
createProjectV2Field(input: {
projectId: $projectId
dataType: SINGLE_SELECT
name: "Priority"
singleSelectOptions: [
{name: "Critical", color: RED, description: "Crash, data loss, security, blocks all users"}
{name: "High", color: ORANGE, description: "Broken workflow, regression, significant UX issue"}
{name: "Medium", color: YELLOW, description: "New feature, improvement, moderate bug"}
{name: "Low", color: BLUE, description: "Cosmetic, nice-to-have, minor, tech debt"}
]
}) {
projectV2Field { ... on ProjectV2SingleSelectField { id name options { id name } } }
}
}
' -f projectId="$PROJECT_ID"
Size (single-select) β same shape, name: "Size" and options
{name: "S", color: GREEN, β¦} {name: "M", color: YELLOW, β¦} {name: "L", color: ORANGE, β¦}.
Start and Target (date fields β one call each):
gh api graphql -f query='
mutation($projectId: ID!) {
createProjectV2Field(input: {projectId: $projectId, dataType: DATE, name: "Start"}) {
projectV2Field { ... on ProjectV2Field { id name } }
}
}
' -f projectId="$PROJECT_ID"
# repeat with name: "Target"
Valid dataType values: TEXT, SINGLE_SELECT, NUMBER, DATE, ITERATION.
Reconcile the Status field (the tricky one)
The auto-created Status field usually has Todo / In Progress / Done; the standard spec adds
Backlog and In Review. Options are edited with updateProjectV2Field, which is a
full replace β you must resend every existing option plus the additions in one array, or
the options you omit are deleted. First read the current options (the Get project fields query
in github-project-management, capturing the Status field's id and each option's name),
then:
gh api graphql -f query='
mutation($fieldId: ID!) {
updateProjectV2Field(input: {
fieldId: $fieldId
singleSelectOptions: [
{name: "Backlog", color: GRAY, description: "Not yet scheduled"}
{name: "Todo", color: BLUE, description: "Scheduled, not started"}
{name: "In Progress", color: YELLOW, description: "Actively being worked"}
{name: "In Review", color: ORANGE, description: "In a PR / awaiting review"}
{name: "Done", color: GREEN, description: "Merged / closed"}
]
}) {
projectV2Field { ... on ProjectV2SingleSelectField { id options { id name } } }
}
}
' -f fieldId="$STATUS_FIELD_ID"
Honest caveats β verify before you run this on a live board:
- Full replace regenerates option IDs. Items already assigned to an option can be orphaned (their Status clears) when the option set is rewritten. This is harmless on a fresh board (no items yet) β the common case for this skill β but on a populated board it can wipe assignments. There, prefer editing the Status options in the GitHub UI, or proceed only with the user's explicit understanding of the risk.
- The built-in Status field is the most restricted β some accounts still reject add/delete/rename of its options via the API. If the mutation errors, fall back to the manual UI steps below rather than fighting it.
- Same read-first, full-array pattern applies to adding a missing option to any existing single-select field (e.g. a Priority someone hand-created without "Critical").
Views β not creatable via the public API
Projects v2 views cannot be created via the public GraphQL API (no createProjectV2View
mutation exists). Two ways to get the Table / Kanban / Roadmap views:
A. Copy a template board (recommended when you keep one). copyProjectV2 clones a source
project's fields, options, and views in one call β so if you maintain a canonical template
board, copy it instead of building field-by-field, then link the copy to the repo:
gh api graphql -f query='
mutation($ownerId: ID!, $projectId: ID!, $title: String!) {
copyProjectV2(input: {
ownerId: $ownerId, projectId: $projectId, title: $title, includeDraftIssues: false
}) {
projectV2 { id number url }
}
}
' -f ownerId="$OWNER_ID" -f projectId="$TEMPLATE_PROJECT_ID" -f title="wafflestack"
B. Guided manual steps (no template). Print these for the user to click through on the
board's web UI (β¦/projects/<number>):
- Table β the default view; rename it "Table" and show all items.
- Kanban β New view β Board layout β group by Status. Columns follow the Status options (Backlog β Todo β In Progress β In Review β Done).
- Roadmap β New view β Roadmap layout β set the date fields to Start and Target.
Verify
Re-run the Get project fields query (github-project-management) and confirm Status,
Priority, Size, Start, and Target are present with the expected options; open the board and
confirm the three views. Report field/option/view coverage vs. the standard spec, and note
anything left to the manual UI (views without a template, or Status options the API refused).
Error handling
- Missing
projectscope βgh auth refresh -s project, then retry (never widen scopes without the user's go-ahead). - GraphQL errors β check the response for
.errors(see thegithub-project-managementerror-handling pattern) and surface the message; don't assume success from a0exit. - Ambiguous discovery (several projects match after normalization) β list them (number + title) and ask which to standardize rather than guessing.
- Board provisioning is a deliberate, user-approved action β unlike the consumer skills' best-effort board sync, do not silently skip on failure; report what succeeded and what still needs a manual step.