From agent-flow
Parses a specification document, applies an epic template, and creates backlog epics in an issue tracker via MCP tools. Supports dry-run validation and optional subtask decomposition.
How this skill is triggered — by the user, by Claude, or both
Slash command
/agent-flow:create-backlog <spec-path> [--decompose] [--update] [--dry-run] [--yolo]<spec-path> [--decompose] [--update] [--dry-run] [--yolo]This skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
Input: `$ARGUMENTS` = spec path (positional) + optional flags (`--decompose`, `--update`, `--dry-run`, `--yolo`)
Input: $ARGUMENTS = spec path (positional) + optional flags (--decompose, --update, --dry-run, --yolo)
If $ARGUMENTS contains --yolo, activate YOLO mode: auto-approve human gates. Note: Gate 3 confirmation can still be overridden by --dry-run.
Read Automation Config from CLAUDE.md section ## Automation Config. Follow ../../core/config-reader.md.
Required:
Optional:
customization/)--decomposeParse $ARGUMENTS:
--decompose, --update, --dry-run, --yolo from the arguments string--decompose and --update are mutually exclusive. If both present: STOP with "Cannot use --decompose with --update."--dry-run can combine with any other flag.If --dry-run, skip MCP check (no tracker writes will occur).
Otherwise, follow ../../core/mcp-preflight.md:
mcp__* tool matching the tracker type is accessible[agent-flow] 🔴 Pipeline Block
Agent: create-backlog
Step: MCP pre-flight check
Reason: Cannot connect to your {Type} issue tracker.
Detail: Expected tool prefix: mcp__{Type}__*. No matching tool is registered in this session.
Recommendation: Run /agent-flow:check-setup for diagnostics, or /agent-flow:setup-mcp to configure the {Type} integration.
Create .agent-flow/backlog-{YYYYMMDD-HHmmss}/ directory.
Initialize state.json with:
{
"schema_version": "1.0",
"run_id": "backlog-{YYYYMMDD-HHmmss}",
"parent_run_id": null,
"mode": "backlog-creation",
"pipeline": "create-backlog",
"status": "running",
"started_at": "{ISO-8601}",
"updated_at": "{ISO-8601}",
"config": {
"profile": null,
"flags": [],
"retry_limits": {
"fixer_iterations": 5,
"test_attempts": 3,
"build_retries": 3
}
},
"backlog": {
"spec_path": "{spec-path}",
"epics_total": 0,
"epics_created": 0,
"epics_failed": 0,
"subtasks_created": 0,
"created_issues": []
}
}
Follow atomic write protocol from ../../core/state-manager.md.
Read the spec path provided in $ARGUMENTS:
{spec-path}/epics/*.md (spec-based scaffold format). If no epics/ subdir exists, glob {spec-path}/*.md.If the path does not exist or is empty: STOP with "Specification path not found or empty: {spec-path}"
Update state.json: write backlog.spec_path. Follow atomic write protocol from ../../core/state-manager.md.
You MUST invoke Task(subagent_type='agent-flow:backlog-creator', model='sonnet'). DO NOT inline-execute.
Context to pass:
{sprint_planning.epic_template} if configured — otherwise omit (agent uses built-in template)Max epics: 10Before dispatch, check Agent Overrides: follow ../../core/agent-override-injector.md for backlog-creator overrides.
If the backlog-creator agent Blocks: display the block message and STOP.
Store from backlog-creator output:
epic_list: structured list of epics (title, scope, AC, size, dependencies, verification)epics_total: count of epics in the listUpdate state.json: write backlog.epics_total. Follow atomic write protocol from ../../core/state-manager.md.
Display the Backlog Summary table from backlog-creator output:
## Backlog Summary
| # | Epic | AC | Size | SP | Dependencies |
|---|------|----|------|----|--------------|
| 1 | {title} | {count} | {XS/S/M/L} | {points} | {deps or "none"} |
If --dry-run:
Prompt: "Create {N} epics in {tracker_type} tracker? [Y/n]"
If --yolo: auto-approve (display "[auto-approved]").
If rejected (user enters n): STOP with "Cancelled. No issues created."
Accumulator pattern — NON-BLOCKING:
SET success_count = 0
SET failure_count = 0
SET created_issues = [] // list of {epic_index, tracker_issue_id, title}
Update mode (--update flag):
Execute the update matching algorithm (see Update Matching section below):
epic_list, compute match against existing issues using:
## Update Preview
| # | Epic | Match | Tracker Issue | Similarity |
|---|------|-------|---------------|------------|
| 1 | {title} | MATCHED | {ID} | {score} |
| 2 | {title} | NEW | -- | -- |
Update {M} existing issue(s) and create {N} new issue(s)? [Y/n]
--yolo: auto-approve. Otherwise wait for confirmation.Create mode (default, and for unmatched epics in update mode):
For each epic in epic_list (or unmatched epics in --update mode):
Build the Epic Card content from the Epic Card Template:
## {Epic Title}
**Type:** feature
**Size:** {XS|S|M|L} ({N} SP)
**Dependencies:** {comma-separated epic titles, or "none"}
### Scope
{2-3 sentences describing what needs to be built}
### Acceptance Criteria
1. {Testable criterion}
2. {Testable criterion}
3. {Testable criterion}
### Verification
- Unit: {what to test with unit tests}
- Integration: {what to test with integration tests}
- E2E: {what to test end-to-end}
If sprint_planning.epic_template is configured and the file exists: use that template instead.
Per-tracker epic creation dispatch:
TRY:
IF tracker_type == "youtrack":
result = mcp__youtrack__create_issue(
project: {issue_tracker.project},
summary: {epic.title},
description: {epic_card_content},
type: "Feature"
)
SET new_id = result.id
ELSE IF tracker_type == "jira":
// Attempt Epic issue type; fall back to Story if Epic unavailable
TRY:
result = mcp__jira__create_issue(
project: {issue_tracker.project},
summary: {epic.title},
description: {epic_card_content},
issuetype: "Epic"
)
CATCH issuetype_error:
LOG WARN "Epic issue type unavailable in Jira project {issue_tracker.project}. Falling back to Story."
result = mcp__jira__create_issue(
project: {issue_tracker.project},
summary: {epic.title},
description: {epic_card_content},
issuetype: "Story"
)
SET new_id = result.key
ELSE IF tracker_type == "linear":
result = mcp__linear__create_issue(
teamId: {issue_tracker.project},
title: {epic.title},
description: {epic_card_content},
labelNames: ["feature"]
)
SET new_id = result.id
ELSE IF tracker_type == "github":
result = mcp__github__create_issue(
owner: {owner from issue_tracker.project},
repo: {repo from issue_tracker.project},
title: {epic.title},
body: {epic_card_content},
labels: ["epic"]
)
SET new_id = result.number
ELSE IF tracker_type == "gitea":
// Gitea: use Bash curl REST API (MCP Gitea does not guarantee epic label support)
owner = {owner from issue_tracker.project}
repo = {repo from issue_tracker.project}
result = Bash(
curl -s -X POST "{issue_tracker.instance}/api/v1/repos/{owner}/{repo}/issues"
-H "Authorization: token $GITEA_TOKEN"
-H "Content-Type: application/json"
-d '{"title":"{epic.title}","body":"{epic_card_content_escaped}","labels":[]}'
)
// If GITEA_TOKEN is not set, fall back to:
// result = mcp__gitea__create_issue
// with parameters: owner, repo, title, body
SET new_id = result.number
ELSE IF tracker_type == "redmine":
result = mcp__redmine__create_issue(
project_id: {issue_tracker.project},
subject: {epic.title},
description: {epic_card_content},
tracker_id: "Feature"
// If "Feature" tracker unavailable, omit tracker_id (use project default)
)
SET new_id = result.id
// --- Write to state ---
ADD {epic_index: {N}, tracker_issue_id: new_id, title: epic.title} to created_issues
success_count += 1
// Update state.json per epic (atomic, immediate)
UPDATE state.json: increment backlog.epics_created, append to backlog.created_issues
Follow atomic write protocol from ../../core/state-manager.md
CATCH error:
LOG WARN "Could not create tracker issue for epic '{epic.title}': {error}"
failure_count += 1
CONTINUE // NON-BLOCKING — proceed to next epic
Per-Tracker Epic Creation Parameters:
| Tracker | MCP Tool Prefix | Title Param | Description Param | Type / Label | Notes |
|---|---|---|---|---|---|
| YouTrack | mcp__youtrack__* | summary | description | type: "Feature" | Top-level issue, no parent |
| Jira | mcp__jira__* or mcp__atlassian__* | summary | description | issuetype: "Epic" | Fallback to "Story" if Epic type unavailable |
| Linear | mcp__linear__* | title | description | labelNames: ["feature"] | No native Epic type; use label |
| GitHub | mcp__github__* | title | body | labels: ["epic"] | Uses REST via MCP |
| Gitea | Bash curl REST or mcp__gitea__* | title | body | labels: ["epic"] | Bash preferred; MCP fallback |
| Redmine | mcp__redmine__* | subject | description | tracker_id: "Feature" | Fallback to project default tracker |
Created {success_count}/{success_count + failure_count} epic issues.
If --decompose and subtasks were created:
Created {subtasks_created} sub-tasks across {epic_count} epics.
If failure_count > 0:
({failure_count} failures. Check warnings above.)
Update state.json: set top-level status to "completed". Follow atomic write protocol from ../../core/state-manager.md.
Only executed if --decompose flag is present.
Run AFTER Step 4 for each successfully created epic issue (i.e., every issue in created_issues).
For each epic in created_issues:
You MUST invoke Task(subagent_type='agent-flow:architect', model='opus'). DO NOT inline-execute.
Epic: {epic.title}\nSpec content:\n{epic_card_content}\nParent tracker issue: {tracker_issue_id}../../core/agent-override-injector.md for architect overridesIf architect blocks: LOG WARN "Architect blocked for epic '{epic.title}': {reason}". Continue to next epic — NON-BLOCKING.
From architect output, extract subtask list. For each subtask:
Build subtask description:
{subtask.scope}
Addresses: {subtask.maps_to[0]}, {subtask.maps_to[1]}, ...
Files: {subtask.files[0]}, {subtask.files[1]}, ...
Parent issue: {tracker_issue_id}
(Omit "Addresses:" line if maps_to is empty. Omit "Files:" line if files is empty. "Parent issue:" always present.)
Create sub-issues using the same per-tracker dispatch table from implement-feature Step 5a (with parent parameter pointing to the epic's tracker_issue_id). Follow the same accumulator pattern (NON-BLOCKING on individual subtask failures).
Update state.json: increment backlog.subtasks_created by the count of successfully created sub-issues. Follow atomic write protocol from ../../core/state-manager.md.
Update mode (--update) matching algorithm:
Normalize title: lowercase, strip leading/trailing whitespace, collapse multiple spaces.
For each epic in epic_list:
For each open tracker issue:
prefix_match = (normalized_epic_title[:40] == normalized_issue_title[:40])
jaccard = |token_intersection| / |token_union| // tokens = split on whitespace+punctuation
match = prefix_match OR jaccard >= 0.7
IF exactly one match: pair epic <-> issue
IF multiple matches: select highest Jaccard. If tied, select most recently updated. WARN.
IF no match: add to unmatched_epics list.
Edge cases:
--decompose): same rule applies to each sub-issueOn start set state transition applied (they represent planned work, not active execution)../../core/agent-override-injector.md for backlog-creator and architect invocationssprint_planning.epic_template is set but the file is missing: WARN and use the built-in Epic Card Template — do not block--dry-run skips MCP pre-flight, skips all tracker writes, and always stops after the preview gate[agent-flow] 🔴 Pipeline Block
Agent: create-backlog
Step: {step where failure occurred}
Reason: {max 2 sentences}
Detail: {technical output}
Recommendation: {what the human should do}
npx claudepluginhub asysta-act/agent-flow --plugin agent-flowAutomatically converts Confluence specs into Jira Epics and implementation tickets, eliminating manual copy-paste.
Creates and manages GitHub epics that group related issues under a common label, tracks progress, and coordinates milestones for large feature-level work.