Break an Epic (or all Epics under a PRD) into atomic Task issues and link them back.
/plugin marketplace add el-feo/ai-context/plugin install ghpm@jebs-dev-toolsResolution order if omitted:
gh issue list -l Epic -s open --limit 1 --json number -q '.[0].number'
</arguments>
<usage_examples> With epic number:
/ghpm:create-tasks epic=#42
With PRD number (creates tasks for all linked Epics):
/ghpm:create-tasks prd=#10
Auto-resolve most recent Epic:
/ghpm:create-tasks
</usage_examples>
<operating_rules>
feat, fix, refactor, etc.) and Scope for conventional commits.<estimation_guidance>
Assign a Fibonacci estimate to each Task based on relative complexity. Estimates reflect effort/complexity, not hours.
| Estimate | Complexity | Examples |
|---|---|---|
| 1 | Trivial | Update README, fix typo, change config value |
| 2 | Simple | Add a single test, update documentation section, simple refactor |
| 3 | Moderate | Add simple feature, refactor small module, add validation |
| 5 | Complex | Multi-file feature, significant refactor, new API endpoint |
| 8 | Very Complex | Cross-cutting feature, complex integration, architectural change |
Tasks estimated >8 MUST be decomposed. If a task would be estimated higher than 8:
</estimation_guidance>
<task_count_guidance>
Match task count to Epic complexity, not a fixed range. Over-decomposition creates overhead without value.
| Epic Scope | Task Count | Example |
|---|---|---|
| Single file, simple change | 1 task | Update README, fix typo, change config |
| Single file, multiple changes | 1-2 tasks | Refactor a module, update docs with verification |
| Multiple related files | 2-4 tasks | Add feature touching model + controller + view |
| Cross-cutting feature | 4-8 tasks | New API endpoint with auth, tests, docs |
| Large architectural change | 8-15 tasks | Database migration, new service layer |
If an Epic affects 1-2 files with a clear scope, 1-2 tasks is usually sufficient.
</task_count_guidance>
<input_validation>
Before proceeding, validate:
# 1. Check gh CLI authentication
gh auth status || { echo "ERROR: Not authenticated. Run 'gh auth login'"; exit 1; }
# 2. Validate issue number format (if provided)
# Epic and PRD numbers must be positive integers
# 3. Verify issue exists and is accessible
gh issue view "$EPIC" > /dev/null 2>&1 || { echo "ERROR: Cannot access issue #$EPIC"; exit 1; }
</input_validation>
<task_issue_format>
Use this streamlined template. Tasks link to Epic only (PRD reachable via Epic).
# Task: <Name>
**Epic:** #<EPIC_NUMBER> | **Type:** `<type>` | **Scope:** `<scope>`
## Objective
<1-2 sentences: what to implement>
## Acceptance Criteria
- [ ] Criterion 1
- [ ] Criterion 2
- [ ] ...
## Test Plan
<How to verify completion - manual steps, test commands, or verification approach>
Omit these sections (redundant or rarely populated):
The Commit Type field determines the conventional commit prefix used during implementation:
| Type | Use When |
|---|---|
feat | Adding new functionality, features, or capabilities |
fix | Fixing bugs, errors, or incorrect behavior |
refactor | Restructuring code without changing behavior |
test | Adding or improving tests only |
docs | Documentation changes only |
chore | Build, CI, dependencies, or tooling changes |
How to determine:
featfixrefactortestdocschore</task_issue_format>
<workflow># If epic=#N provided, use N
EPIC={provided_epic_number}
# Else if prd=#N provided, find Epics as sub-issues of the PRD
# NOTE: Use heredoc to avoid shell escaping issues with '!' characters
OWNER=$(gh repo view --json owner -q '.owner.login')
REPO=$(gh repo view --json name -q '.name')
cat > /tmp/ghpm-subissues.graphql << 'GRAPHQL'
query($owner: String!, $repo: String!, $number: Int!) {
repository(owner: $owner, name: $repo) {
issue(number: $number) {
subIssues(first: 50) {
nodes {
number
title
state
labels(first: 10) {
nodes { name }
}
}
}
}
}
}
GRAPHQL
gh api graphql -F owner="$OWNER" -F repo="$REPO" -F number=$PRD \
-f query="$(cat /tmp/ghpm-subissues.graphql)" \
--jq '.data.repository.issue.subIssues.nodes[] | select(.state == "OPEN") | select(.labels.nodes[].name == "Epic") | [.number, .title] | @tsv'
# Else pick most recent open Epic
gh issue list -l Epic -s open --limit 1 --json number -q '.[0].number'
For each Epic:
# Fetch Epic body and metadata
gh issue view "$EPIC" --json title,body,url,labels -q '.'
Extract from Epic:
PRD: #123 or similar pattern)For each Epic, generate the appropriate number of atomic tasks based on the task count guidance above.
Create each task:
# Create the task issue
TASK_URL=$(gh issue create \
--title "Task: <Name>" \
--label "Task" \
--body "<Task markdown from template>")
# Extract task number from URL
TASK_NUM=$(echo "$TASK_URL" | grep -oE '[0-9]+$')
# Set the estimate value determined during task planning (1, 2, 3, 5, or 8)
ESTIMATE=<fibonacci_number> # Replace with actual estimate, e.g., ESTIMATE=3
If $GHPM_PROJECT is set (project number), add the task to the project and set the Estimate field:
# Add task to project and set estimate (if GHPM_PROJECT is set)
if [ -n "$GHPM_PROJECT" ]; then
# Add to project (GHPM_PROJECT should be the project number, e.g., "7")
ITEM_ID=$(gh project item-add "$GHPM_PROJECT" --owner "$OWNER" --url "$TASK_URL" --format json 2>/dev/null | jq -r '.id')
if [ -n "$ITEM_ID" ] && [ "$ITEM_ID" != "null" ]; then
# Get project ID and Estimate field ID
PROJECT_DATA=$(gh project view "$GHPM_PROJECT" --owner "$OWNER" --format json 2>/dev/null)
PROJECT_ID=$(echo "$PROJECT_DATA" | jq -r '.id')
ESTIMATE_FIELD_ID=$(gh project field-list "$GHPM_PROJECT" --owner "$OWNER" --format json 2>/dev/null | jq -r '.fields[] | select(.name == "Estimate") | .id')
if [ -n "$ESTIMATE_FIELD_ID" ] && [ "$ESTIMATE_FIELD_ID" != "null" ]; then
# Set estimate value (ESTIMATE is the Fibonacci number assigned to this task)
gh project item-edit --project-id "$PROJECT_ID" --id "$ITEM_ID" --field-id "$ESTIMATE_FIELD_ID" --number "$ESTIMATE" 2>/dev/null \
&& echo "✓ Set estimate $ESTIMATE for Task #$TASK_NUM" \
|| echo "WARNING: Could not set estimate for Task #$TASK_NUM"
else
echo "WARNING: Estimate field not found in project. Add a Number field named 'Estimate'."
fi
else
echo "WARNING: Could not add Task #$TASK_NUM to project '$GHPM_PROJECT'"
fi
fi
IMPORTANT: Tasks MUST be linked as sub-issues of the Epic, not just listed in a comment.
For each created task, link it as a sub-issue:
# Get the Epic's internal issue ID
EPIC_ID=$(gh api repos/{owner}/{repo}/issues/$EPIC --jq .id)
# Get the Task's internal issue ID
TASK_ID=$(gh api repos/{owner}/{repo}/issues/$TASK_NUM --jq .id)
# Add task as sub-issue of Epic
gh api repos/{owner}/{repo}/issues/$EPIC/sub_issues \
-X POST \
-F sub_issue_id=$TASK_ID \
--silent || echo "Warning: Could not link Task #$TASK_NUM as sub-issue"
After all tasks are created and linked, optionally comment on the Epic with a summary:
gh issue comment "$EPIC" --body "$(cat <<'EOF'
## Tasks Created
Created and linked as sub-issues:
- #<TASK_1> Task: <Name>
- #<TASK_2> Task: <Name>
...
View sub-issues in the Epic's "Sub-issues" section.
EOF
)"
If PRD is known, comment on the PRD (one comment per Epic, avoid spam):
gh issue comment "$PRD" --body "Tasks created for Epic #$EPIC - see checklist on the Epic."
</workflow>
<error_handling> If gh CLI not authenticated:
gh auth statusgh auth loginIf Epic/PRD not found:
If issue creation fails:
gh api rate_limitIf sub-issue linking fails:
gh api repos/{owner}/{repo}/issues/$EPIC/sub_issuesIf project association fails:
$GHPM_PROJECT value is correctIf Estimate field is missing from project:
If task would exceed estimate of 8:
<success_criteria> Command completes successfully when:
GHPM_PROJECT set, Estimate field is populated (or warning issued if field missing)Verification:
# List created tasks
gh issue list -l Task -s open --limit 50 --json number,title
# Verify sub-issues are linked to Epic
gh api repos/{owner}/{repo}/issues/$EPIC/sub_issues --jq '.[] | [.number, .title] | @tsv'
# View Epic to confirm (sub-issues appear in issue view)
gh issue view "$EPIC"
</success_criteria>
<output> After completion, report:Epic(s) processed: # and URL for each
Tasks created: Issue numbers, URLs, and estimates
| Task | Title | Estimate |
|---|---|---|
| #N | Task: Name | 3 |
Sub-issue linking: Success/failure for each task linked to Epic
Total tasks: Count per Epic, total estimate points
Project association: Success/failure status (if $GHPM_PROJECT set)
Estimate field: Success/failure for setting estimates (if $GHPM_PROJECT set)
Warnings: Any issues encountered (e.g., sub-issue linking failed, project add failed, Estimate field missing)
</output>Proceed now.