Manage GitHub Project boards — issues, sprints, status tracking, acceptance verification, and release management. White-labeled template for any project.
Manage issues and project board from Claude Code. Provides full lifecycle management from roadmap ideas through sprint execution to completion, including acceptance criteria verification. Supports multiple issue tracking backends via pluggable providers.
This skill uses a provider pattern to support multiple issue tracking systems through a unified interface. Each provider implements the same CLI contract, so all workflow rules (transitions, guards, formatting) work identically regardless of backend.
| Provider | Backend | CLI Tool | Status |
|----------|---------|----------|--------|
| github | GitHub Projects + Issues | gh | Full support |
| jira | Jira Cloud / Data Center | curl | Full support |
| youtrack | YouTrack Cloud / Standalone | curl | Full support |
All API operations go through provider-specific scripts in this skill's providers/ directory:
# Source config to get provider setting
source ./cognitive-core.conf 2>/dev/null || source ./.claude/cognitive-core.conf
# Find and use the active provider script
PB_PROVIDER="${CC_PROJECT_BOARD_PROVIDER:-github}"
PB_SCRIPT=$(find . -path "*/project-board/providers/${PB_PROVIDER}.sh" -type f 2>/dev/null | head -1)
# All providers share the same CLI interface:
$PB_SCRIPT issue list [--priority P] [--area A] [--state S]
$PB_SCRIPT issue create "title" [--labels L] [--body B]
$PB_SCRIPT issue close <N> [--comment C]
$PB_SCRIPT issue reopen <N>
$PB_SCRIPT issue view <N> [--json fields]
$PB_SCRIPT issue comment <N> "body"
$PB_SCRIPT issue assign <N> <user>
$PB_SCRIPT board summary
$PB_SCRIPT board status <N>
$PB_SCRIPT board move <N> <status_key>
$PB_SCRIPT board add <N>
$PB_SCRIPT board approve <N> [--comment C]
$PB_SCRIPT board blocked <N> [--reason R] [--by N2]
$PB_SCRIPT board unblock <N> [--comment C]
$PB_SCRIPT board metrics [--sprint S]
$PB_SCRIPT sprint list [--all]
$PB_SCRIPT sprint assign "sprint-title" <N> [N2 N3...]
$PB_SCRIPT branch create <N> <type> <slug> [--base B]
$PB_SCRIPT provider info
All provider output is JSON for consistent parsing. The SKILL.md handles workflow rules, transition validation, and output formatting — providers handle only API translation.
The board workflow is designed as a three-layer architecture that keeps vendor-specific logic isolated:
┌─────────────────────────────────────────────────┐
│ Layer 1: SKILL.md (Workflow Rules) │
│ - Transition matrix, WIP limits, approval gate │
│ - Epic decomposition, closure guard │
│ - Metrics computation, output formatting │
│ - 100% vendor-agnostic │
├─────────────────────────────────────────────────┤
│ Layer 2: _provider-lib.sh (Shared Contract) │
│ - CLI interface (issue, board, sprint, branch) │
│ - JSON I/O protocol │
│ - Provider validation and routing │
├─────────────────────────────────────────────────┤
│ Layer 3: providers/*.sh (Vendor Adapters) │
│ - github.sh → GitHub Projects V2 GraphQL API │
│ - jira.sh → Jira REST API (Cloud + DC) │
│ - youtrack.sh → YouTrack REST API │
│ - Future: azure.sh, linear.sh, shortcut.sh │
└─────────────────────────────────────────────────┘
Key design rules:
CC_* variables are provider-agnostic; vendor-specific settings use CC_GITHUB_*, CC_JIRA_*, CC_YOUTRACK_* prefixesAdding a new provider (e.g., Azure DevOps):
providers/azure.sh implementing the CLI contractCC_AZURE_* configuration variablesCC_AZURE_STATUS_MAP# In cognitive-core.conf:
CC_PROJECT_BOARD_PROVIDER="github" # github|jira|youtrack
CC_GITHUB_OWNER="owner" # e.g., "wolaschka"
CC_GITHUB_REPO="owner/repo" # e.g., "wolaschka/TIMS"
CC_PROJECT_NUMBER=3 # GitHub Project number
CC_PROJECT_ID="PVT_xxx" # GraphQL Project ID
CC_STATUS_FIELD_ID="PVTSSF_xxx" # Status field ID
CC_AREA_FIELD_ID="PVTSSF_xxx" # Area field ID (optional)
CC_SPRINT_FIELD_ID="PVTIF_xxx" # Sprint iteration field ID (optional)
CC_JIRA_URL="https://company.atlassian.net" # Jira Cloud or Data Center URL
CC_JIRA_PROJECT="PROJ" # Project key
CC_JIRA_EMAIL="user@company.com" # Account email (Cloud auth)
CC_JIRA_TOKEN="api-token" # API token (Cloud) or PAT (Data Center)
CC_JIRA_AUTH_TYPE="basic" # basic (Cloud) or bearer (Data Center)
CC_JIRA_BOARD_ID="" # Agile board ID (optional, for sprints)
CC_JIRA_STATUS_MAP="roadmap=To Do|backlog=Backlog|todo=To Do|progress=In Progress|testing=In Review|done=Done|canceled=Canceled"
CC_YOUTRACK_URL="https://company.youtrack.cloud" # YouTrack URL
CC_YOUTRACK_PROJECT="PROJ" # Project short name
CC_YOUTRACK_TOKEN="perm:token" # Permanent token
CC_YOUTRACK_AGILE_ID="" # Agile board ID (optional, for sprints)
CC_YOUTRACK_STATUS_MAP="roadmap=No State|backlog=Open|todo=To Do|progress=In Progress|testing=To Verify|done=Done|canceled=Canceled"
CC_REQUIRE_HUMAN_APPROVAL="true" # Stop at To Be Tested, require /approve
CC_REQUIRE_DIFFERENT_APPROVER="false" # SOX: approver must differ from assignee
CC_REQUIRED_APPROVERS="1" # Number of approvals needed (1 or 2)
CC_WIP_LIMIT_PROGRESS="0" # Max issues In Progress (0 = unlimited)
CC_WIP_LIMIT_TESTING="0" # Max issues in To Be Tested (0 = unlimited)
CC_WIP_LIMIT_TODO="0" # Max issues in Todo (0 = unlimited)
CC_BRANCH_AUTO_CREATE="false" # Auto-create branch on move to In Progress
CC_BRANCH_AUTO_CHECKOUT="true" # Auto-checkout the created branch locally
CC_BRANCH_BASE="main" # Base branch for feature/fix branches
CC_BRANCH_HOTFIX_BASE="main" # Base branch for hotfix branches
CC_BRANCH_DEFAULT_TYPE="feature" # Default type when no label matches
CC_BRANCH_SLUG_MAX_LENGTH="40" # Max slug length in branch names
CC_BRANCH_LABEL_MAP="bug=fix|enhancement=feature|documentation=docs"
CRITICAL: Before ANY GraphQL mutation that references a projectId, verify it matches the configured CC_PROJECT_ID exactly. Users often have multiple GitHub projects and field IDs from the wrong project will silently add/move items to unrelated boards.
Validation rules:
projectId values in mutations MUST equal CC_PROJECT_IDfieldId) MUST belong to the configured project — they typically contain a substring of the project IDgh project field-list, ALWAYS specify --owner CC_GITHUB_OWNER and the correct CC_PROJECT_NUMBERgh project field-list response returns IDs that don't match the expected project ID substring, ABORT and report the mismatchIf wrong project is detected: Stop immediately and report: "Wrong project detected — field IDs do not match configured project. Aborting to prevent cross-project contamination."
Roadmap → Backlog → Todo → In Progress → To Be Tested → Done
↘ Canceled
| Column | Meaning | Sprint Required | |--------|---------|-----------------| | Roadmap | Feature ideas and future enhancements | No | | Backlog | Accepted work, ready for sprint planning | No | | Todo | Committed to a sprint, not yet started | Yes | | In Progress | Actively being developed | Yes | | To Be Tested | Code complete, needs verification | Yes | | Done | Verified and closed (terminal) | — | | Canceled | Abandoned or deferred (terminal) | — |
Any active issue (Todo, In Progress, To Be Tested) can be flagged as blocked. Blocked is a label, not a column — the issue stays in its current column but is visually marked.
Set blocked:
gh issue edit <N> --repo {{CC_GITHUB_REPO}} --add-label "blocked"
gh issue comment <N> --repo {{CC_GITHUB_REPO}} --body "Blocked: <reason>. Waiting on: <dependency>"
Clear blocked:
gh issue edit <N> --repo {{CC_GITHUB_REPO}} --remove-label "blocked"
gh issue comment <N> --repo {{CC_GITHUB_REPO}} --body "Unblocked: <resolution>"
Blocked dependency tracking: Use the convention Blocked-by: #N in the blocking comment. When the blocking issue is resolved, the move command should prompt to unblock dependent issues.
Sprint impact: Blocked items count against WIP limits but should be flagged in sprint reviews as impediments.
When configured, the move command enforces Work-in-Progress limits per column. This prevents context-switching overload and makes bottlenecks visible.
| Setting | Column | Default |
|---------|--------|---------|
| CC_WIP_LIMIT_TODO | Todo | 0 (unlimited) |
| CC_WIP_LIMIT_PROGRESS | In Progress | 0 (unlimited) |
| CC_WIP_LIMIT_TESTING | To Be Tested | 0 (unlimited) |
Enforcement: Before moving an issue into a WIP-limited column, count current items in that column. If at limit:
--force to suppress the warning.blocked label do not count against WIP limits (they are impediments, not active work).Recommended limits (per team member):
When CC_REQUIRE_HUMAN_APPROVAL="true" (default), automated workflows stop at "To Be Tested" instead of auto-closing to "Done". This provides:
The coordinator agent posts verification evidence (acceptance criteria table, deployment screenshots, code references) and leaves the issue open for human review. Use /project-board approve <number> to accept and close.
Set CC_REQUIRE_HUMAN_APPROVAL="false" for fully autonomous workflows.
When CC_REQUIRE_DIFFERENT_APPROVER="true":
When CC_REQUIRED_APPROVERS="2" (dual approval):
CC_REQUIRE_DIFFERENT_APPROVER="true")Replace with your project's actual IDs after running setup.sh:
roadmap → {{STATUS_ROADMAP_ID}}
backlog → {{STATUS_BACKLOG_ID}}
todo → {{STATUS_TODO_ID}}
progress → {{STATUS_PROGRESS_ID}}
testing → {{STATUS_TESTING_ID}}
done → {{STATUS_DONE_ID}}
canceled → {{STATUS_CANCELED_ID}}
Based on Linear/Jira/Kanban best practices. The move command MUST enforce these rules.
FROM → TO Roadmap Backlog Todo In Progress To Be Tested Done Canceled
─────────────────────────────────────────────────────────────────────────────────────
Roadmap - ✓ ✓ - - - ✓
Backlog ✓ - ✓ - - - ✓
Todo - ✓ - ✓ - - ✓
In Progress - ✓* ✓* - ✓ - ✓
To Be Tested - - - ✓* - ✓ ✓
Done - - - ✓* ✓* - -
Canceled - ✓* ✓* - - - -
✓ = Allowed | ✓* = Allowed but warn (deprioritize/reopen/rework) | - = Blocked
move command automatically reopens the GitHub issue when moving out of Done or Canceled.move command automatically runs gh issue reopen to sync the GitHub issue state with the board status.move command automatically assigns the issue to the current sprint if it has no sprint set. Requires CC_SPRINT_FIELD_ID to be configured.The project-board-automation.yml workflow (in cicd/workflows/) handles:
Closes #N → issue moves to In Progress (from Todo only)REQUIRE_HUMAN_APPROVAL=true) or Done (when false)REQUIRE_HUMAN_APPROVAL=true) or Done (when false)/project-board approve path)Customizable per project. Default domains:
| Area | Scope | Option ID |
|------|-------|-----------|
| CI/CD | Build pipeline, containers, deployment | {{AREA_CICD_ID}} |
| Monitoring | Metrics, alerting, dashboards | {{AREA_MONITORING_ID}} |
| Testing | Test framework, coverage, QA | {{AREA_TESTING_ID}} |
| Security | Access control, scanning, encryption | {{AREA_SECURITY_ID}} |
| Infrastructure | Servers, backup, networking | {{AREA_INFRASTRUCTURE_ID}} |
sprint command to view current sprint progresssprint-plan to assign issues to iterations| Type | Values |
|------|--------|
| Priority | priority:p0-critical, priority:p1-high, priority:p2-medium, priority:p3-low |
| Area | area:cicd, area:monitoring, area:testing, area:security, area:infrastructure |
| Kind | bug, enhancement, documentation |
Parse the user's arguments to determine which command to run. Default (no args) = list.
list (default)List open issues grouped by priority.
gh issue list --repo {{CC_GITHUB_REPO}} --state open --label "priority:p0-critical" --json number,title,labels,assignees
gh issue list --repo {{CC_GITHUB_REPO}} --state open --label "priority:p1-high" --json number,title,labels,assignees
gh issue list --repo {{CC_GITHUB_REPO}} --state open --label "priority:p2-medium" --json number,title,labels,assignees
gh issue list --repo {{CC_GITHUB_REPO}} --state open --label "priority:p3-low" --json number,title,labels,assignees
Format as priority-grouped table:
## Open Issues
### P0 — Critical
| # | Title | Area | Assignee |
|---|-------|------|----------|
### P1 — High
...
Support --area=<area> filter (adds --label "area:<area>") and --state=closed (changes to --state closed --limit 10).
createSyntax: /project-board create "title" [--priority p0|p1|p2|p3] [--area cicd|monitoring|testing|security|infrastructure] [--body "description"] [--plan <path>]
Map --priority pN to labels: p0→priority:p0-critical, p1→priority:p1-high, p2→priority:p2-medium, p3→priority:p3-low.
Map --area to label area:<value>.
gh issue create --repo {{CC_GITHUB_REPO}} --title "<title>" --label "<labels>" --body "<body>"
ISSUE_ID=$(gh issue view <number> --repo {{CC_GITHUB_REPO}} --json id --jq '.id')
ITEM_ID=$(gh api graphql -f query='mutation { addProjectV2ItemById(input: { projectId: "{{CC_PROJECT_ID}}" contentId: "'$ISSUE_ID'" }) { item { id } } }' --jq '.data.addProjectV2ItemById.item.id')
# Set Area field (map --area value to the matching Area Option ID)
gh api graphql -f query='mutation { updateProjectV2ItemFieldValue(input: { projectId: "{{CC_PROJECT_ID}}" itemId: "'$ITEM_ID'" fieldId: "{{CC_AREA_FIELD_ID}}" value: { singleSelectOptionId: "<AREA_OPTION_ID>" } }) { projectV2Item { id } } }'
Default status: Backlog (unless --status specified)
Attach implementation plan (if --plan provided or CC_ISSUE_ATTACH_PLAN=true):
If --plan <path> is provided, read the file and post it as a comment on the newly created issue:
gh issue comment <number> --repo {{CC_GITHUB_REPO}} --body "$(cat <<'PLAN'
## Implementation Plan
$(cat <plan-path>)
---
*Attached by `/project-board create`. Source: `<plan-path>`*
PLAN
)"
If no --plan flag but CC_ISSUE_ATTACH_PLAN=true, check for an active plan file in ~/.claude/plans/. If exactly one .md file exists, attach it automatically. If multiple exist, skip (ambiguous).
closeSyntax: /project-board close <number> [number2 ...] [--comment "reason"]
Closure Guard: If the issue has acceptance criteria (checkbox list in body), run verification FIRST. NEVER close an issue that has PARTIAL or FAIL criteria. If any criteria are not PASS, block the close and report the gaps. This prevents premature closure that hides unfinished work.
gh issue close <number> --repo {{CC_GITHUB_REPO}} --comment "<comment>"
ITEMS=$(gh project item-list {{CC_PROJECT_NUMBER}} --owner {{CC_GITHUB_OWNER}} --format json --limit 500)
ITEM_ID=$(echo "$ITEMS" | jq -r --argjson n <N> '.items[] | select(.content.number == $n) | .id')
gh api graphql -f query='mutation { updateProjectV2ItemFieldValue(input: { projectId: "{{CC_PROJECT_ID}}" itemId: "'$ITEM_ID'" fieldId: "{{CC_STATUS_FIELD_ID}}" value: { singleSelectOptionId: "{{STATUS_DONE_ID}}" } }) { projectV2Item { id } } }'
cancelCancel one or more issues. Moves to Canceled on the board. Requires a reason.
Syntax: /project-board cancel <number> [number2 ...] --reason "why"
# Check current status first
ITEMS=$(gh project item-list {{CC_PROJECT_NUMBER}} --owner {{CC_GITHUB_OWNER}} --format json --limit 500)
CURRENT=$(echo "$ITEMS" | jq -r --argjson n <N> '.items[] | select(.content.number == $n) | .status')
# Block if Done
if [ "$CURRENT" = "Done" ]; then echo "Cannot cancel a Done issue. Create a new issue instead."; exit 1; fi
# Close with reason
gh issue close <number> --repo {{CC_GITHUB_REPO}} --comment "Canceled: <reason>"
# Set board status to Canceled
ITEM_ID=$(echo "$ITEMS" | jq -r --argjson n <N> '.items[] | select(.content.number == $n) | .id')
gh api graphql -f query='mutation { updateProjectV2ItemFieldValue(input: { projectId: "{{CC_PROJECT_ID}}" itemId: "'$ITEM_ID'" fieldId: "{{CC_STATUS_FIELD_ID}}" value: { singleSelectOptionId: "{{STATUS_CANCELED_ID}}" } }) { projectV2Item { id } } }'
assignSyntax: /project-board assign <number> <username>
gh issue edit <number> --repo {{CC_GITHUB_REPO}} --add-assignee <username>
sprintShow current sprint progress. Query the Sprint iteration field, filter items, group by status.
# Get sprint iterations
gh api graphql -f query='query {
user(login: "{{CC_GITHUB_OWNER}}") {
projectV2(number: {{CC_PROJECT_NUMBER}}) {
field(name: "Sprint") {
... on ProjectV2IterationField {
configuration { iterations { id title startDate duration } }
}
}
}
}
}'
# List all items with sprint and status
gh project item-list {{CC_PROJECT_NUMBER}} --owner {{CC_GITHUB_OWNER}} --format json
Filter items matching current iteration. Group by status:
## Sprint: <title> (<date range>)
### In Progress
| # | Title | Area | Assignee |
### To Be Tested
| # | Title | Area | Pending |
### Done
| # | Title | Area |
### Not started
| # | Title | Area |
**Progress**: X/Y items done (Z%)
Support --all (show all sprints) and --backlog (include unassigned items).
sprint-planSyntax: /project-board sprint-plan "<sprint-title>" <issue-numbers...>
Example: /project-board sprint-plan "Sprint 2" 22 23 24
gh api graphql -f query='query {
user(login: "{{CC_GITHUB_OWNER}}") {
projectV2(number: {{CC_PROJECT_NUMBER}}) {
field(name: "Sprint") {
... on ProjectV2IterationField {
configuration { iterations { id title startDate duration } }
}
}
}
}
}' --jq '.data.user.projectV2.field.configuration.iterations[] | select(.title == "<SPRINT_TITLE>") | .id'
If sprint doesn't exist, create a new iteration via updateProjectV2 mutation. New iterations get startDate = previous sprint endDate, same duration (14 days).
For each issue, get its pr
Search for places (restaurants, cafes, etc.) via Google Places API proxy on localhost.
Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Start voice calls via the OpenClaw voice-call plugin.
Notion API for creating and managing pages, databases, and blocks.
Gemini CLI for one-shot Q&A, summaries, and generation.
Category:developer