| name | Managing GitHub Issue Hierarchies |
| description | Manages GitHub issue parent-child relationships and sub-issues using REST and GraphQL APIs. Use when breaking down large work into Epics/Features/Tasks, creating work breakdown structures, converting markdown checklists to sub-issues, reordering sub-issues, or querying issue hierarchies. Handles ID confusion between REST numeric IDs and GraphQL node IDs. |
GitHub Issue Hierarchy
Manage GitHub issue parent-child relationships (sub-issues) using the REST and GraphQL APIs.
What you should do
When invoked, help the user manage issue hierarchies by:
Understanding the request - Determine what hierarchy operation is needed:
- Add sub-issue to parent
- Remove sub-issue from parent
- Query issue hierarchy (show tree)
- Convert checklist items to sub-issues
- Reorder sub-issues
- Move sub-issue to different parent
Get issue information - If not provided, ask the user:
- Which issue is the parent?
- Which issue(s) should be sub-issues?
- Repository owner and name (if not in current repo)
Execute the appropriate operation based on the request type.
Core operations
1. Add sub-issue to parent
Using REST API:
# Get the issue ID (not node_id) from REST API
PARENT_ID=$(gh api "repos/OWNER/REPO/issues/PARENT_NUMBER" --jq '.id')
SUB_ISSUE_ID=$(gh api "repos/OWNER/REPO/issues/SUB_NUMBER" --jq '.id')
# Add sub-issue
gh api --method POST "repos/OWNER/REPO/issues/$PARENT_ID/sub_issues" \
-f sub_issue_id="$SUB_ISSUE_ID"
Using GraphQL:
# Get node IDs
PARENT_NODE_ID=$(gh api graphql -f query='
query {
repository(owner: "OWNER", name: "REPO") {
issue(number: PARENT_NUMBER) { id }
}
}' --jq '.data.repository.issue.id')
SUB_NODE_ID=$(gh api graphql -f query='
query {
repository(owner: "OWNER", name: "REPO") {
issue(number: SUB_NUMBER) { id }
}
}' --jq '.data.repository.issue.id')
# Add sub-issue
gh api graphql -f query='
mutation {
addSubIssue(input: {
issueId: "'$PARENT_NODE_ID'"
subIssueId: "'$SUB_NODE_ID'"
}) {
issue {
number
title
}
}
}'
2. Remove sub-issue from parent
Using REST API:
# Get IDs
PARENT_ID=$(gh api "repos/OWNER/REPO/issues/PARENT_NUMBER" --jq '.id')
SUB_ISSUE_ID=$(gh api "repos/OWNER/REPO/issues/SUB_NUMBER" --jq '.id')
# Remove sub-issue
gh api --method DELETE "repos/OWNER/REPO/issues/$PARENT_ID/sub_issues/$SUB_ISSUE_ID"
Using GraphQL:
gh api graphql -f query='
mutation {
removeSubIssue(input: {
issueId: "'$PARENT_NODE_ID'"
subIssueId: "'$SUB_NODE_ID'"
}) {
issue {
number
title
}
}
}'
3. Get parent of an issue
Using REST API:
gh api "repos/OWNER/REPO/issues/ISSUE_NUMBER/parent" --jq '{number: .number, title: .title}'
4. List all sub-issues of a parent
Using GraphQL:
gh api graphql -f query='
query {
repository(owner: "OWNER", name: "REPO") {
issue(number: PARENT_NUMBER) {
number
title
subIssues(first: 50) {
nodes {
number
title
state
}
totalCount
}
}
}
}' --jq '.data.repository.issue'
5. Query full hierarchy tree
Get complete hierarchy including nested levels:
gh api graphql -f query='
query {
repository(owner: "OWNER", name: "REPO") {
issue(number: PARENT_NUMBER) {
number
title
subIssues(first: 50) {
nodes {
number
title
state
subIssues(first: 50) {
nodes {
number
title
state
}
}
}
}
}
}
}'
6. Reorder sub-issues
Change position in parent's sub-issue list:
# Move sub-issue to specific position (0-indexed)
PARENT_ID=$(gh api "repos/OWNER/REPO/issues/PARENT_NUMBER" --jq '.id')
SUB_ISSUE_ID=$(gh api "repos/OWNER/REPO/issues/SUB_NUMBER" --jq '.id')
gh api --method PATCH "repos/OWNER/REPO/issues/$PARENT_ID/sub_issues/$SUB_ISSUE_ID" \
-f position=0 # Move to first position
Key concepts
Terminology
- Parent issue: Top-level issue that contains sub-issues (often an Epic or Feature)
- Sub-issue: Child issue that is part of a parent's work breakdown
- Hierarchy: Tree structure of parent-child relationships
- Node ID: Global GitHub identifier starting with
I_(for GraphQL) - Numeric ID: Integer ID from REST API (different from node_id!)
Important notes
ID confusion:
- REST API uses numeric
idfield (integer) - GraphQL uses
idfield (string starting withI_) gh issue view --json idreturns node_id, NOT the REST numeric ID- Always fetch IDs using the appropriate API method
Inheritance:
- Sub-issues inherit Project and Milestone from parent by default
- Cross-organization sub-issues are supported
- Sub-issues can have their own sub-issues (nested hierarchy)
Common patterns
Work breakdown structure:
Epic #100: Add authentication system
├─ Feature #101: Implement JWT tokens
├─ Feature #102: Add login UI
│ ├─ Task #103: Design login form
│ └─ Task #104: Add validation
└─ Feature #105: Add session management
Release planning:
Release #200: v2.0.0
├─ #201: Feature A
├─ #202: Feature B
└─ #203: Bug fixes
Milestone tracking:
Milestone #300: Q1 Goals
├─ #301: Improve performance
├─ #302: Add monitoring
└─ #303: Security audit
Complete workflow examples
Example 1: Create Epic with sub-tasks
# Create parent Epic
EPIC_NUMBER=$(gh issue create \
--title "Add authentication system" \
--body "Epic for implementing user authentication" \
--label "epic" \
--json number --jq '.number')
# Create sub-issues
TASK1=$(gh issue create --title "Implement JWT tokens" --json number --jq '.number')
TASK2=$(gh issue create --title "Add login UI" --json number --jq '.number')
TASK3=$(gh issue create --title "Add session management" --json number --jq '.number')
# Add as sub-issues
EPIC_ID=$(gh api "repos/OWNER/REPO/issues/$EPIC_NUMBER" --jq '.id')
for task in $TASK1 $TASK2 $TASK3; do
TASK_ID=$(gh api "repos/OWNER/REPO/issues/$task" --jq '.id')
gh api --method POST "repos/OWNER/REPO/issues/$EPIC_ID/sub_issues" \
-f sub_issue_id="$TASK_ID"
done
echo "✅ Created Epic #$EPIC_NUMBER with 3 sub-tasks"
Example 2: Convert checklist to sub-issues
# Given an issue with checklist in body:
# - [ ] Implement JWT tokens
# - [ ] Add login UI
# - [ ] Add session management
PARENT_NUMBER=100
PARENT_ID=$(gh api "repos/OWNER/REPO/issues/$PARENT_NUMBER" --jq '.id')
# Extract checklist items and create sub-issues
gh issue view $PARENT_NUMBER --json body --jq '.body' | \
grep -E '^\s*-\s+\[ \]' | \
sed 's/^\s*-\s*\[ \]\s*//' | \
while read -r task; do
echo "Creating: $task"
ISSUE_NUM=$(gh issue create --title "$task" --json number --jq '.number')
ISSUE_ID=$(gh api "repos/OWNER/REPO/issues/$ISSUE_NUM" --jq '.id')
gh api --method POST "repos/OWNER/REPO/issues/$PARENT_ID/sub_issues" \
-f sub_issue_id="$ISSUE_ID"
done
Example 3: Show hierarchy tree
# Display parent with all sub-issues
gh api graphql -f query='
query {
repository(owner: "OWNER", name: "REPO") {
issue(number: 100) {
number
title
subIssues(first: 50) {
nodes {
number
title
state
}
totalCount
}
}
}
}' --jq '.data.repository.issue |
"Parent: #\(.number) - \(.title)\n" +
"Sub-issues (\(.subIssues.totalCount)):\n" +
(.subIssues.nodes | map(" ├─ #\(.number) [\(.state)] \(.title)") | join("\n"))'
Example 4: Move sub-issue to different parent
# Remove from old parent
OLD_PARENT_ID=$(gh api "repos/OWNER/REPO/issues/100" --jq '.id')
SUB_ISSUE_ID=$(gh api "repos/OWNER/REPO/issues/150" --jq '.id')
gh api --method DELETE "repos/OWNER/REPO/issues/$OLD_PARENT_ID/sub_issues/$SUB_ISSUE_ID"
# Add to new parent
NEW_PARENT_ID=$(gh api "repos/OWNER/REPO/issues/200" --jq '.id')
gh api --method POST "repos/OWNER/REPO/issues/$NEW_PARENT_ID/sub_issues" \
-f sub_issue_id="$SUB_ISSUE_ID"
echo "✅ Moved #150 from #100 to #200"
Error handling
Common issues:
"Resource not accessible by integration"
- Requires write access to repository
- Check authentication and permissions
"Issue is already a sub-issue"
- An issue can only have one parent at a time
- Remove from current parent before adding to new one
"Circular dependency detected"
- Cannot make a parent a sub-issue of its own child
- Check hierarchy before creating relationships
"Invalid issue ID"
- Ensure using numeric
idfor REST API (notnode_id) - Fetch IDs using correct API method
- Ensure using numeric
"Sub-issue limit exceeded"
- GitHub may limit number of sub-issues per parent
- Consider flattening hierarchy or grouping differently
Best practices
When to use sub-issues:
- Breaking down Epics into Features/Tasks
- Organizing release checklists
- Tracking multi-step implementation work
- Creating hierarchical roadmaps
When NOT to use sub-issues:
- Simple task lists (use markdown checklist instead)
- Temporary relationships (use labels or milestones)
- Blocking relationships (use dependencies - see
gh-issue-dependencies)
Hierarchy design:
- Keep hierarchies shallow (2-3 levels max)
- Use consistent naming (Epic → Feature → Task)
- Close parent only when all children are closed
- Use labels to indicate hierarchy level (epic, feature, task)
Combining with other features:
- Use dependencies for ordering (Task A blocks Task B)
- Use milestones for time-based grouping
- Use labels for categorization
- Use projects for status tracking
Related concepts
Sub-issues vs Dependencies
Sub-issues (this skill):
- Decompose work: "What are the parts of this issue?"
- Parent-child hierarchy
- Epic → Features → Tasks
- Example: Epic "Add auth" has sub-issues "JWT", "Login UI", "Sessions"
Dependencies (see gh-issue-dependencies skill):
- Ordering constraints: "What must happen before this?"
- Peer-to-peer blocking relationships
- Can span unrelated features
- Example: "API schema" blocks "API implementation"
These can be combined:
Epic #100: Add authentication
├─ #101: JWT tokens (blocked by #200 API schema design)
├─ #102: Login UI
└─ #103: Sessions (blocked by #101)
Integration with workflows
Works well with:
gh-issue-dependenciesskill - Add blocking relationships between sub-issuesgh-project-manageskill - Track sub-issue progress in projectsgh-issue-typesskill - Assign types (Epic, Feature, Task) to hierarchy levelsgit-issue-createagent - Create parent and children in one workflow
Automation ideas:
- Auto-create sub-issues from issue template checklists
- Auto-close parent when all children are closed
- Auto-inherit labels from parent to children
- Generate progress reports (X of Y sub-issues completed)
- Create release notes from Epic hierarchies
Example usage patterns
Recommended hierarchy levels:
Epic (parent) - Large feature or initiative
- Label:
epic - Value: Essential or Useful
- Effort: Heavy
- Project field: Set manually
- Label:
Feature (child of Epic) - Distinct deliverable
- Label:
feature - Inherits project from Epic
- Set own Value/Effort
- Label:
Task (child of Feature) - Implementation step
- Label:
task - Inherits project from Feature
- Effort: Light or Moderate
- Label:
Example hierarchy:
Epic #100: User authentication system
├─ Feature #101: Add OAuth integration
├─ Feature #102: Add two-factor authentication
└─ Feature #103: Update session management