git-issue-hierarchy

v2026.09.24

GitHub sub-issues and blocked_by/blocking links. Use when breaking an issue into sub-tasks, checking parent progress, or viewing a dependency graph.

GitHub
Install command
npx skhub add laurigates/git-issue-hierarchy
Markdown
SKILL.md

When to Use This Skill

Use this skill when...Use the alternative when...
Adding/removing native GitHub sub-issues to a parent issueUse git-issue-manage for transfer, pin, lock, develop-branch operations
Marking issue A as blocked_by issue B (or unblocking)Use github-issue-writing to create well-structured issue bodies in the first place
Viewing a parent issue's sub-issue completion progress and dependency graphUse git-issue to actually start working on issues end-to-end
Checking the dependency graph before starting work on a multi-issue featureUse gh-cli-agentic for raw gh issue --json queries without hierarchy logic

Context

  • Repo: !git remote -v
  • Parent issue: (parsed from arguments)

Parameters

Parse these parameters from the command:

Every issue argument — <parent-issue> and each <N> flag value below — is accepted as a bare number, #N, or a full GitHub issue URL, and normalized to a (number, repo) pair in Step 0.

ParameterDescription
<parent-issue>Parent issue as a bare number, #N, or full GitHub issue URL (https://github.com/<owner>/<repo>/issues/<N>) — see Step 0
--add <N...>Add existing issues as sub-issues (each N as number, #N, or URL)
--create "<title>"Create a new issue and add it as sub-issue
--remove <N...>Remove sub-issues from parent (each N as number, #N, or URL)
--statusShow sub-issue completion progress
--listList all sub-issues of the parent
--depsShow dependency graph (blocked_by + blocking + sub-issues) for the issue
--blockingList issues the parent is blocking
--block <N>Mark issue N as blocked by the parent (parent blocks N); N as number, #N, or URL
--blocked-by <N>Mark the parent as blocked by issue N; N as number, #N, or URL
--unblock <N>Remove blocking relationship with issue N in either direction; N as number, #N, or URL

When to Use

Use this skill when...Use X instead when...
Breaking issues into sub-tasksCreating standalone issues (github-issue-writing)
Checking sub-issue completion progressImplementing/processing issues (git:issue)
Recording blocked_by / blocking dependenciesAuto-detecting related issues (github-issue-autodetect)
Viewing a blocker graph before picking workSearching for OSS solutions (github-issue-search)

Sub-issues vs. dependencies vs. "related to"

GitHub ships three distinct ways to link issues. Pick the right one — they're not interchangeable:

RelationshipWhen to useAPI surface
Sub-issue (parent ↔ child)Child issue is a part of the parent's scope. Completing all children fulfils the parent.issues/{N}/sub_issues
Blocked by (hard dependency)Parent cannot start or ship until the other issue closes. Makes the blocked issue render a "Blocked" badge on boards.issues/{N}/dependencies/blocked_by
Blocking (read-only inverse)You want to see everything this issue gates. Managed by creating blocked_by links on the other side.issues/{N}/dependencies/blocking
"Related to #N" in bodySoft cross-reference, no lifecycle coupling, no board indicator.Plain markdown — no API needed

Sub-issues express composition ("is part of"). Dependencies express ordering ("must happen before"). The same two issues should rarely use both — a sub-issue is implicitly ordered by its parent's scope.

Execution

Execute the requested issue hierarchy operation.

Step 0: Normalize issue references

Before any API call, normalize <parent-issue> and every issue value passed to a flag (--add, --remove, --block, --blocked-by, --unblock) into a (number, repo) pair. Accept these forms:

Input formExtract
123number 123, repo = current remote
#123number 123, repo = current remote
https://github.com/<owner>/<repo>/issues/123number 123, repo = <owner>/<repo>
.../issues/123#issuecomment-...number 123 (drop the #... fragment), repo = <owner>/<repo>

Rules:

  1. Strip a leading #; strip a URL #... fragment after the number. A token is an issue ref only if, after stripping, it is all digits or matches the /issues/<digits> URL shape (require trailing digits — a /pull/<N>, /discussions/<N>, or bare /issues list URL is not a ref).
  2. For the plural flags (--add/--remove, which take <N...>), normalize each space-separated value independently; never split a single URL into two refs.
  3. For a URL whose <owner>/<repo> differs from the current remote (Step 1 REPO), record it as cross-repo and carry -R <owner>/<repo> on every gh issue call and target repos/<owner>/<repo>/… on every gh api call for that issue. Sub-issue and dependency links require both endpoints to live in the same repo — if a normalized ref points at a different repo than the parent, surface that rather than issuing a mismatched cross-repo link.

Downstream steps use the normalized $PARENT, $N, and $CHILD numbers; where a ref was cross-repo, substitute its <owner>/<repo> for $OWNER/$REPO_NAME and add -R <owner>/<repo> to the corresponding gh issue call.

Step 1: Resolve Repository Context

REPO=$(gh repo view --json nameWithOwner --jq '.nameWithOwner')
OWNER=$(echo "$REPO" | cut -d/ -f1)
REPO_NAME=$(echo "$REPO" | cut -d/ -f2)

If Step 0 normalized <parent-issue> to a cross-repo URL, use that URL's <owner>/<repo> as $OWNER/$REPO_NAME (and pass -R <owner>/<repo> to the gh issue view below) instead of the current remote.

Verify the parent issue exists:

gh issue view $PARENT --json number,title,state,subIssuesSummary

Step 2: Branch on Operation Mode

Determine which operation to perform based on parsed parameters.

If --status (or no flags): Display sub-issue summary and list.

If --add: Add existing issues as sub-issues.

If --create: Create new issue, then add as sub-issue.

If --remove: Remove specified sub-issues.

If --list: List all sub-issues with their states.

If --deps, --blocking, --block, --blocked-by, --unblock: Manage native GitHub issue dependencies via the dependencies/blocked_by and dependencies/blocking API endpoints.

Step 3: Execute API Calls

Sub-Issue Status

# Get summary
gh issue view $PARENT --json title,state,subIssuesSummary

# List all sub-issues with details
gh api repos/$OWNER/$REPO_NAME/issues/$PARENT/sub_issues \
  --jq '.[] | "#\(.number) \(.state) \(.title)"'

Report format:

Issue #42: Refactor authentication system
Sub-issues: 3/5 completed (60%)

  #43 ✓ Extract token validation
  #44 ✓ Add refresh token support
  #45 ✓ Update OAuth provider
  #46 ○ Migrate session storage
  #47 ○ Update API documentation

Add Sub-Issues

For each issue number in --add:

# Get the issue's node ID (required for sub_issue_id)
CHILD_ID=$(gh api repos/$OWNER/$REPO_NAME/issues/$CHILD --jq '.id')

# Add as sub-issue
gh api repos/$OWNER/$REPO_NAME/issues/$PARENT/sub_issues \
  -f sub_issue_id=$CHILD_ID

Verify each was added successfully. Report any errors (e.g., issue not found, already a sub-issue, sub-issues not enabled).

Create and Add Sub-Issue

# Create the new issue
NEW_ISSUE=$(gh issue create --title "$TITLE" --body "Parent: #$PARENT" --json number --jq '.number')

# Get its ID
NEW_ID=$(gh api repos/$OWNER/$REPO_NAME/issues/$NEW_ISSUE --jq '.id')

# Add as sub-issue
gh api repos/$OWNER/$REPO_NAME/issues/$PARENT/sub_issues \
  -f sub_issue_id=$NEW_ID

Remove Sub-Issues

For each issue number in --remove:

# Get the sub-issue ID from the sub-issues list
SUB_ISSUE_ID=$(gh api repos/$OWNER/$REPO_NAME/issues/$PARENT/sub_issues \
  --jq ".[] | select(.number == $CHILD) | .id")

# Remove it
gh api repos/$OWNER/$REPO_NAME/issues/$PARENT/sub_issues/$SUB_ISSUE_ID -X DELETE

Dependency Management

Dependencies use GitHub's native dependencies/blocked_by and dependencies/blocking endpoints. They appear in the issue sidebar under "Relationships" and mark the blocked issue with a "Blocked" badge on project boards. Both endpoints require the target issue's node id (.id on the issue payload), not the human-readable issue number.

Add "blocked by" relationship (--blocked-by <N>): parent is blocked by N

# Resolve the blocker's node id
BLOCKER_ID=$(gh api repos/$OWNER/$REPO_NAME/issues/$N --jq '.id')

# Record the dependency on the parent
gh api repos/$OWNER/$REPO_NAME/issues/$PARENT/dependencies/blocked_by \
  -f issue_id=$BLOCKER_ID

Add "blocks" relationship (--block <N>): parent blocks issue N

The API is one-directional — write the relationship on the blocked side:

# Resolve the parent's node id
PARENT_ID=$(gh api repos/$OWNER/$REPO_NAME/issues/$PARENT --jq '.id')

# Record on issue N that it is blocked by the parent
gh api repos/$OWNER/$REPO_NAME/issues/$N/dependencies/blocked_by \
  -f issue_id=$PARENT_ID

Remove relationship (--unblock <N>):

Look up which side carries the link, then delete it. The DELETE path takes the stored dependency's {issue_id} segment:

# Is the parent blocked by N?
gh api repos/$OWNER/$REPO_NAME/issues/$PARENT/dependencies/blocked_by \
  --jq ".[] | select(.number == $N) | .id"

# Or does the parent block N?
gh api repos/$OWNER/$REPO_NAME/issues/$N/dependencies/blocked_by \
  --jq ".[] | select(.number == $PARENT) | .id"

# Delete whichever is present
gh api repos/$OWNER/$REPO_NAME/issues/$ISSUE/dependencies/blocked_by/$DEP_ID \
  -X DELETE

List what the parent blocks (--blocking):

gh api repos/$OWNER/$REPO_NAME/issues/$PARENT/dependencies/blocking \
  --jq '.[] | "#\(.number) \(.state) \(.title)"'

Show dependency graph (--deps):

Combine both dependency endpoints with the sub-issues summary. Do not parse issue bodies — the native API is authoritative:

# What blocks the parent
gh api repos/$OWNER/$REPO_NAME/issues/$PARENT/dependencies/blocked_by \
  --jq '.[] | "#\(.number) \(.state) \(.title)"'

# What the parent blocks
gh api repos/$OWNER/$REPO_NAME/issues/$PARENT/dependencies/blocking \
  --jq '.[] | "#\(.number) \(.state) \(.title)"'

# Sub-issues (composition, not ordering)
gh api repos/$OWNER/$REPO_NAME/issues/$PARENT/sub_issues \
  --jq '.[] | "#\(.number) \(.state) \(.title)"'

Render output as:

#42 Refactor authentication
├── Blocked by: #40 Database migration (✓ closed)
├── Blocks:     #45 Deploy auth v2 (○ open)
└── Sub-issues:
    ├── #43 ✓ Extract token validation
    └── #44 ○ Add refresh token support

Surface Blocked by entries that are still open prominently — those are what prevent the parent from starting.

Step 4: Report Results

Report what was done:

OperationReport Format
--statusSummary with completion percentage and sub-issue list
--addConfirmation of each added sub-issue
--createNew issue number + confirmation added as sub-issue
--removeConfirmation of each removed sub-issue
--depsDependency tree visualization (blocked_by + blocking + sub-issues)
--blockingList of issues the parent blocks
--block/--blocked-byConfirmation of relationship added, rendered with direction
--unblockConfirmation of relationship removed

Error Handling

ErrorCauseAction
404 on sub_issues endpointSub-issues not enabled for repoReport: "Sub-issues are not available for this repository. Enable them in repository settings."
404 on dependencies endpointIssue dependencies feature not enabled for repo/orgReport: "Issue dependencies are not available for this repository. Ask an owner to enable them under Repository settings → Features → Issues."
422 on add sub-issueIssue already a sub-issue or circular referenceReport the specific error
422 on add dependencyCircular dependency, already linked, or self-referenceReport the specific error
Issue not foundInvalid issue numberReport which issue number was not found

Agentic Optimizations

ContextCommand
Quick sub-issue statusgh issue view N --json title,subIssuesSummary
List sub-issuesgh api repos/{o}/{r}/issues/{N}/sub_issues --jq '.[].number'
Add sub-issuegh api repos/{o}/{r}/issues/{N}/sub_issues -f sub_issue_id=M
Remove sub-issuegh api repos/{o}/{r}/issues/{N}/sub_issues/M -X DELETE
List blockersgh api repos/{o}/{r}/issues/{N}/dependencies/blocked_by --jq '.[].number'
List blocked-by-megh api repos/{o}/{r}/issues/{N}/dependencies/blocking --jq '.[].number'
Add blockergh api repos/{o}/{r}/issues/{N}/dependencies/blocked_by -f issue_id=<node-id>
Remove blockergh api repos/{o}/{r}/issues/{N}/dependencies/blocked_by/{dep_id} -X DELETE
Resolve node idgh api repos/{o}/{r}/issues/{N} --jq '.id'

See Also

  • github-issue-writing skill for creating standalone issues
  • git:issue skill for implementing/processing issues
  • gh-cli-agentic skill for raw API patterns
Discovery
Tags

No tags published for this skill.

Version
Latest version metadata

Version

v2026.09.24

Published

Sep 24, 2026

Category

Uncategorized

License

MIT

Source path

git-plugin/skills/git-issue-hierarchy

Default branch

main

Latest commit

1668324

Tree SHA

b2d4cc3