claude-autopm 1.20.0 → 1.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +255 -878
- package/autopm/.claude/agents/README.md +1 -1
- package/autopm/.claude/agents/decision-matrices/python-backend-selection.md +25 -25
- package/autopm/.claude/agents/decision-matrices/ui-framework-selection.md +43 -43
- package/autopm/.claude/agents/devops/github-operations-specialist.md +1 -1
- package/autopm/.claude/agents/frameworks/README.md +5 -5
- package/autopm/.claude/agents/frameworks/e2e-test-engineer.md +1 -1
- package/autopm/.claude/agents/frameworks/nats-messaging-expert.md +1 -1
- package/autopm/.claude/agents/frameworks/react-frontend-engineer.md +1 -1
- package/autopm/.claude/agents/frameworks/react-ui-expert.md +3 -3
- package/autopm/.claude/agents/frameworks/tailwindcss-expert.md +3 -3
- package/autopm/.claude/agents/frameworks/ux-design-expert.md +3 -3
- package/autopm/.claude/commands/infrastructure/traefik-setup.md +1 -1
- package/autopm/.claude/commands/playwright/test-scaffold.md +1 -1
- package/autopm/.claude/commands/pm/epic-sync.md +37 -4
- package/autopm/.claude/commands/ui/bootstrap-scaffold.md +6 -5
- package/autopm/.claude/commands/ui/tailwind-system.md +1 -1
- package/autopm/.claude/examples/mcp/playwright-mcp.md +2 -2
- package/autopm/.claude/examples/mcp-servers.example.json +2 -2
- package/autopm/.claude/hooks/docker-first-enforcement.sh +1 -1
- package/autopm/.claude/mcp/playwright-mcp.md +2 -2
- package/autopm/.claude/rules/agent-coordination.md +26 -24
- package/autopm/.claude/rules/docker-first-development.md +1 -1
- package/autopm/.claude/rules/framework-path-rules.md +180 -0
- package/autopm/.claude/rules/infrastructure-pipeline.md +1 -1
- package/autopm/.claude/rules/ui-development-standards.md +1 -1
- package/autopm/.claude/rules/visual-testing.md +3 -3
- package/autopm/.claude/scripts/pm/epic-sync/README.md +208 -0
- package/autopm/.claude/scripts/pm/epic-sync/create-epic-issue.sh +68 -192
- package/autopm/.claude/scripts/pm/epic-sync/create-task-issues.sh +60 -328
- package/autopm/.claude/scripts/pm/epic-sync/update-epic-file.sh +61 -354
- package/autopm/.claude/scripts/pm/epic-sync/update-references.sh +67 -305
- package/autopm/.claude/scripts/pm/epic-sync.sh +137 -0
- package/autopm/.claude/teams.json +3 -5
- package/autopm/.claude/templates/claude-templates/addons/devops-agents.md +2 -2
- package/autopm/.claude/templates/claude-templates/addons/docker-agents.md +4 -4
- package/autopm/.claude/templates/claude-templates/addons/minimal-agents.md +1 -1
- package/autopm/.claude/templates/issue-decomposition/api.yaml +2 -2
- package/autopm/.claude/templates/issue-decomposition/auth.yaml +4 -4
- package/autopm/.claude/templates/issue-decomposition/crud.yaml +3 -3
- package/autopm/.claude/templates/issue-decomposition/default.yaml +1 -1
- package/autopm/.claude/templates/issue-decomposition/ui-feature.yaml +2 -2
- package/package.json +4 -3
- package/scripts/validate-framework-paths.sh +104 -0
|
@@ -1,201 +1,77 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
-
# Create Epic Issue
|
|
3
|
-
#
|
|
2
|
+
# Create Epic Issue
|
|
3
|
+
# Creates the main GitHub issue for an epic with proper labels and stats
|
|
4
4
|
|
|
5
5
|
set -euo pipefail
|
|
6
6
|
|
|
7
|
-
# Load libraries
|
|
8
7
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
# Script configuration
|
|
15
|
-
readonly EPIC_NAME="${1:-}"
|
|
16
|
-
|
|
17
|
-
# Main function
|
|
18
|
-
main() {
|
|
19
|
-
print_banner "Epic Issue Creator" "1.0.0"
|
|
20
|
-
|
|
21
|
-
# Validate inputs
|
|
22
|
-
log_info "Validating epic: $EPIC_NAME"
|
|
23
|
-
validate_epic_name "$EPIC_NAME" || exit 1
|
|
24
|
-
validate_epic_structure "$EPIC_NAME" || exit 1
|
|
25
|
-
|
|
26
|
-
# Check GitHub authentication and repo protection
|
|
27
|
-
validate_github_auth || exit 1
|
|
28
|
-
check_repo_protection || exit 1
|
|
29
|
-
|
|
30
|
-
local epic_file=".claude/epics/$EPIC_NAME/epic.md"
|
|
31
|
-
local temp_dir="/tmp/epic-sync-$$"
|
|
32
|
-
|
|
33
|
-
log_info "Creating temporary workspace: $temp_dir"
|
|
34
|
-
mkdir -p "$temp_dir"
|
|
35
|
-
|
|
36
|
-
# Ensure cleanup on exit
|
|
37
|
-
trap "rm -rf '$temp_dir'" EXIT
|
|
38
|
-
|
|
39
|
-
# Process epic content
|
|
40
|
-
with_error_handling "Strip frontmatter from epic" \
|
|
41
|
-
strip_frontmatter "$epic_file" "$temp_dir/epic-body-raw.md"
|
|
42
|
-
|
|
43
|
-
with_error_handling "Process epic content and stats" \
|
|
44
|
-
process_epic_content "$temp_dir/epic-body-raw.md" "$temp_dir/epic-body.md"
|
|
45
|
-
|
|
46
|
-
with_error_handling "Determine epic type" \
|
|
47
|
-
epic_type=$(determine_epic_type "$temp_dir/epic-body.md")
|
|
48
|
-
|
|
49
|
-
with_error_handling "Create GitHub epic issue" \
|
|
50
|
-
issue_number=$(create_epic_github_issue "$temp_dir/epic-body.md" "$epic_type")
|
|
51
|
-
|
|
52
|
-
# Output result
|
|
53
|
-
local repo_info
|
|
54
|
-
repo_info=$(get_repo_info)
|
|
55
|
-
local repo_name
|
|
56
|
-
repo_name=$(echo "$repo_info" | grep -o '"nameWithOwner":"[^"]*"' | cut -d'"' -f4)
|
|
57
|
-
|
|
58
|
-
log_success "Epic issue created successfully!"
|
|
59
|
-
echo "Epic Issue: #${issue_number}"
|
|
60
|
-
echo "URL: https://github.com/${repo_name}/issues/${issue_number}"
|
|
61
|
-
echo "$issue_number" # For script consumption
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
# Process epic content and replace Tasks Created section with Stats
|
|
65
|
-
process_epic_content() {
|
|
66
|
-
local input_file="$1"
|
|
67
|
-
local output_file="$2"
|
|
68
|
-
|
|
69
|
-
log_function_entry "process_epic_content" "$input_file" "$output_file"
|
|
70
|
-
|
|
71
|
-
# Count tasks in epic directory
|
|
72
|
-
local epic_dir=".claude/epics/$EPIC_NAME"
|
|
73
|
-
local total_tasks parallel_tasks sequential_tasks total_effort
|
|
74
|
-
|
|
75
|
-
total_tasks=$(find "$epic_dir" -name '[0-9][0-9][0-9].md' -type f | wc -l)
|
|
76
|
-
parallel_tasks=$(grep -l '^parallel: true' "$epic_dir"/[0-9][0-9][0-9].md 2>/dev/null | wc -l || echo 0)
|
|
77
|
-
sequential_tasks=$((total_tasks - parallel_tasks))
|
|
78
|
-
|
|
79
|
-
# Try to calculate total effort from task files
|
|
80
|
-
total_effort=$(calculate_total_effort "$epic_dir")
|
|
81
|
-
|
|
82
|
-
# Process content with awk to replace Tasks Created section
|
|
83
|
-
awk -v total_tasks="$total_tasks" \
|
|
84
|
-
-v parallel_tasks="$parallel_tasks" \
|
|
85
|
-
-v sequential_tasks="$sequential_tasks" \
|
|
86
|
-
-v total_effort="$total_effort" '
|
|
87
|
-
/^## Tasks Created/ {
|
|
88
|
-
in_tasks=1
|
|
89
|
-
next
|
|
90
|
-
}
|
|
91
|
-
/^## / && in_tasks {
|
|
92
|
-
in_tasks=0
|
|
93
|
-
# Add Stats section
|
|
94
|
-
print "## Stats"
|
|
95
|
-
print ""
|
|
96
|
-
print "Total tasks: " total_tasks
|
|
97
|
-
print "Parallel tasks: " parallel_tasks " (can be worked on simultaneously)"
|
|
98
|
-
print "Sequential tasks: " sequential_tasks " (have dependencies)"
|
|
99
|
-
if (total_effort) print "Estimated total effort: " total_effort " hours"
|
|
100
|
-
print ""
|
|
101
|
-
}
|
|
102
|
-
/^Total tasks:/ && in_tasks { next }
|
|
103
|
-
/^Parallel tasks:/ && in_tasks { next }
|
|
104
|
-
/^Sequential tasks:/ && in_tasks { next }
|
|
105
|
-
/^Estimated total effort:/ && in_tasks { next }
|
|
106
|
-
!in_tasks { print }
|
|
107
|
-
END {
|
|
108
|
-
# If we were still in tasks section at EOF, add stats
|
|
109
|
-
if (in_tasks) {
|
|
110
|
-
print "## Stats"
|
|
111
|
-
print ""
|
|
112
|
-
print "Total tasks: " total_tasks
|
|
113
|
-
print "Parallel tasks: " parallel_tasks " (can be worked on simultaneously)"
|
|
114
|
-
print "Sequential tasks: " sequential_tasks " (have dependencies)"
|
|
115
|
-
if (total_effort) print "Estimated total effort: " total_effort " hours"
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
' "$input_file" > "$output_file"
|
|
119
|
-
|
|
120
|
-
log_debug "Processed epic content with stats: $total_tasks total, $parallel_tasks parallel"
|
|
121
|
-
log_function_exit "process_epic_content"
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
# Calculate total effort from task files
|
|
125
|
-
calculate_total_effort() {
|
|
126
|
-
local epic_dir="$1"
|
|
127
|
-
local total_effort=0
|
|
128
|
-
|
|
129
|
-
for task_file in "$epic_dir"/[0-9][0-9][0-9].md; do
|
|
130
|
-
[[ -f "$task_file" ]] || continue
|
|
131
|
-
|
|
132
|
-
local effort
|
|
133
|
-
effort=$(get_frontmatter_field "$task_file" "effort" 2>/dev/null || echo "")
|
|
134
|
-
|
|
135
|
-
if [[ "$effort" =~ ^[0-9]+$ ]]; then
|
|
136
|
-
total_effort=$((total_effort + effort))
|
|
137
|
-
fi
|
|
138
|
-
done
|
|
139
|
-
|
|
140
|
-
if [[ "$total_effort" -gt 0 ]]; then
|
|
141
|
-
echo "$total_effort"
|
|
142
|
-
fi
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
# Determine epic type from content
|
|
146
|
-
determine_epic_type() {
|
|
147
|
-
local content_file="$1"
|
|
148
|
-
|
|
149
|
-
log_function_entry "determine_epic_type" "$content_file"
|
|
150
|
-
|
|
151
|
-
if grep -qi "bug\|fix\|issue\|problem\|error\|hotfix\|patch" "$content_file"; then
|
|
152
|
-
echo "bug"
|
|
153
|
-
else
|
|
154
|
-
echo "feature"
|
|
155
|
-
fi
|
|
156
|
-
|
|
157
|
-
log_function_exit "determine_epic_type"
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
# Create GitHub epic issue with proper labels
|
|
161
|
-
create_epic_github_issue() {
|
|
162
|
-
local body_file="$1"
|
|
163
|
-
local epic_type="$2"
|
|
164
|
-
|
|
165
|
-
log_function_entry "create_epic_github_issue" "$body_file" "$epic_type"
|
|
166
|
-
|
|
167
|
-
local title="Epic: $EPIC_NAME"
|
|
168
|
-
local labels="epic,epic:$EPIC_NAME,$epic_type"
|
|
169
|
-
|
|
170
|
-
local issue_number
|
|
171
|
-
issue_number=$(create_github_issue "$title" "$body_file" "$labels")
|
|
172
|
-
|
|
173
|
-
log_function_exit "create_epic_github_issue" 0
|
|
174
|
-
echo "$issue_number"
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
# Error handling
|
|
178
|
-
handle_error() {
|
|
179
|
-
local exit_code=$?
|
|
180
|
-
log_error "Script failed with exit code: $exit_code"
|
|
181
|
-
log_error "Epic issue creation failed for: $EPIC_NAME"
|
|
182
|
-
exit "$exit_code"
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
# Set up error handling
|
|
186
|
-
trap handle_error ERR
|
|
187
|
-
|
|
188
|
-
# Validate arguments
|
|
189
|
-
if [[ $# -ne 1 ]]; then
|
|
8
|
+
EPIC_NAME="${1:-}"
|
|
9
|
+
|
|
10
|
+
if [[ -z "$EPIC_NAME" ]]; then
|
|
11
|
+
echo "❌ Error: Epic name required"
|
|
190
12
|
echo "Usage: $0 <epic_name>"
|
|
191
|
-
echo ""
|
|
192
|
-
echo "Creates a GitHub issue for the specified epic."
|
|
193
|
-
echo ""
|
|
194
|
-
echo "Example:"
|
|
195
|
-
echo " $0 authentication"
|
|
196
|
-
echo ""
|
|
197
13
|
exit 1
|
|
198
14
|
fi
|
|
199
15
|
|
|
200
|
-
#
|
|
201
|
-
|
|
16
|
+
# Source utilities if they exist
|
|
17
|
+
if [[ -f "$SCRIPT_DIR/../../lib/github-utils.sh" ]]; then
|
|
18
|
+
source "$SCRIPT_DIR/../../lib/github-utils.sh"
|
|
19
|
+
fi
|
|
20
|
+
|
|
21
|
+
EPIC_FILE=".claude/epics/$EPIC_NAME/epic.md"
|
|
22
|
+
|
|
23
|
+
if [[ ! -f "$EPIC_FILE" ]]; then
|
|
24
|
+
echo "❌ Error: Epic file not found: $EPIC_FILE"
|
|
25
|
+
exit 1
|
|
26
|
+
fi
|
|
27
|
+
|
|
28
|
+
# Check GitHub CLI
|
|
29
|
+
if ! command -v gh &> /dev/null; then
|
|
30
|
+
echo "❌ Error: GitHub CLI (gh) not installed"
|
|
31
|
+
exit 1
|
|
32
|
+
fi
|
|
33
|
+
|
|
34
|
+
# Check authentication
|
|
35
|
+
if ! gh auth status &> /dev/null; then
|
|
36
|
+
echo "❌ Error: GitHub CLI not authenticated. Run: gh auth login"
|
|
37
|
+
exit 1
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
# Strip frontmatter and get content
|
|
41
|
+
epic_content=$(awk 'BEGIN{p=0} /^---$/{p++; next} p==2{print}' "$EPIC_FILE")
|
|
42
|
+
|
|
43
|
+
# Count tasks
|
|
44
|
+
task_count=$(find ".claude/epics/$EPIC_NAME" -name "[0-9]*.md" -type f 2>/dev/null | wc -l)
|
|
45
|
+
|
|
46
|
+
# Detect epic type (bug vs feature)
|
|
47
|
+
if echo "$epic_content" | grep -qi "bug\|fix\|error\|issue"; then
|
|
48
|
+
labels="epic,bug"
|
|
49
|
+
else
|
|
50
|
+
labels="epic,feature"
|
|
51
|
+
fi
|
|
52
|
+
|
|
53
|
+
# Create issue
|
|
54
|
+
echo "📝 Creating epic issue for: $EPIC_NAME"
|
|
55
|
+
echo " Tasks: $task_count"
|
|
56
|
+
echo " Labels: $labels"
|
|
57
|
+
|
|
58
|
+
# Create the issue
|
|
59
|
+
epic_number=$(gh issue create \
|
|
60
|
+
--title "Epic: $EPIC_NAME" \
|
|
61
|
+
--body "$epic_content
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
**Epic Statistics:**
|
|
65
|
+
- Tasks: $task_count
|
|
66
|
+
- Status: Planning
|
|
67
|
+
- Created: $(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
68
|
+
" \
|
|
69
|
+
--label "$labels" \
|
|
70
|
+
2>&1 | grep -oP '(?<=#)[0-9]+' | head -1)
|
|
71
|
+
|
|
72
|
+
if [[ -z "$epic_number" ]]; then
|
|
73
|
+
echo "❌ Error: Failed to create epic issue"
|
|
74
|
+
exit 1
|
|
75
|
+
fi
|
|
76
|
+
|
|
77
|
+
echo "$epic_number"
|
|
@@ -1,354 +1,86 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
-
# Create Task Issues
|
|
3
|
-
# Creates GitHub issues for all tasks
|
|
2
|
+
# Create Task Issues
|
|
3
|
+
# Creates GitHub issues for all tasks and generates mapping file
|
|
4
4
|
|
|
5
5
|
set -euo pipefail
|
|
6
6
|
|
|
7
|
-
# Load libraries
|
|
8
7
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
source "${SCRIPT_DIR}/../../lib/frontmatter-utils.sh"
|
|
12
|
-
source "${SCRIPT_DIR}/../../lib/validation-utils.sh"
|
|
8
|
+
EPIC_NAME="${1:-}"
|
|
9
|
+
EPIC_NUMBER="${2:-}"
|
|
13
10
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
# Global variables
|
|
20
|
-
declare -g use_subissues=false
|
|
21
|
-
declare -g temp_dir=""
|
|
22
|
-
declare -g mapping_file=""
|
|
23
|
-
|
|
24
|
-
# Main function
|
|
25
|
-
main() {
|
|
26
|
-
print_banner "Task Issues Creator" "1.0.0"
|
|
27
|
-
|
|
28
|
-
# Validate inputs
|
|
29
|
-
log_info "Validating inputs: epic=$EPIC_NAME, epic_number=$EPIC_NUMBER"
|
|
30
|
-
validate_inputs || exit 1
|
|
31
|
-
|
|
32
|
-
# Setup workspace
|
|
33
|
-
setup_workspace
|
|
34
|
-
|
|
35
|
-
# Check GitHub capabilities
|
|
36
|
-
check_github_capabilities
|
|
37
|
-
|
|
38
|
-
# Get task files and determine strategy
|
|
39
|
-
local task_files
|
|
40
|
-
readarray -t task_files < <(find ".claude/epics/$EPIC_NAME" -name '[0-9][0-9][0-9].md' -type f | sort)
|
|
41
|
-
local task_count=${#task_files[@]}
|
|
42
|
-
|
|
43
|
-
log_info "Found $task_count task files"
|
|
44
|
-
|
|
45
|
-
if [[ $task_count -eq 0 ]]; then
|
|
46
|
-
log_error "No task files found in epic directory"
|
|
47
|
-
echo "❌ No tasks to sync. Run: /pm:epic-decompose $EPIC_NAME"
|
|
48
|
-
exit 1
|
|
49
|
-
fi
|
|
50
|
-
|
|
51
|
-
# Create issues based on count
|
|
52
|
-
if [[ $task_count -lt $PARALLEL_THRESHOLD ]]; then
|
|
53
|
-
log_info "Using sequential creation for $task_count tasks"
|
|
54
|
-
create_tasks_sequential "${task_files[@]}"
|
|
55
|
-
else
|
|
56
|
-
log_info "Using parallel creation for $task_count tasks"
|
|
57
|
-
create_tasks_parallel "${task_files[@]}"
|
|
58
|
-
fi
|
|
59
|
-
|
|
60
|
-
# Output results
|
|
61
|
-
display_results "$task_count"
|
|
62
|
-
|
|
63
|
-
# Return mapping file path for further processing
|
|
64
|
-
echo "$mapping_file"
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
# Validate script inputs
|
|
68
|
-
validate_inputs() {
|
|
69
|
-
log_function_entry "validate_inputs"
|
|
70
|
-
|
|
71
|
-
validate_epic_name "$EPIC_NAME" || return 1
|
|
72
|
-
validate_issue_number "$EPIC_NUMBER" || return 1
|
|
73
|
-
validate_epic_structure "$EPIC_NAME" || return 1
|
|
74
|
-
validate_github_auth || return 1
|
|
75
|
-
|
|
76
|
-
log_function_exit "validate_inputs"
|
|
77
|
-
return 0
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
# Setup temporary workspace
|
|
81
|
-
setup_workspace() {
|
|
82
|
-
log_function_entry "setup_workspace"
|
|
83
|
-
|
|
84
|
-
temp_dir="/tmp/task-issues-$$"
|
|
85
|
-
mapping_file="$temp_dir/task-mapping.txt"
|
|
86
|
-
|
|
87
|
-
log_info "Creating workspace: $temp_dir"
|
|
88
|
-
mkdir -p "$temp_dir"
|
|
89
|
-
|
|
90
|
-
# Initialize mapping file
|
|
91
|
-
> "$mapping_file"
|
|
92
|
-
|
|
93
|
-
# Cleanup on exit
|
|
94
|
-
trap "cleanup_workspace" EXIT
|
|
95
|
-
|
|
96
|
-
log_function_exit "setup_workspace"
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
# Cleanup workspace
|
|
100
|
-
cleanup_workspace() {
|
|
101
|
-
if [[ -n "$temp_dir" ]] && [[ -d "$temp_dir" ]]; then
|
|
102
|
-
log_debug "Cleaning up workspace: $temp_dir"
|
|
103
|
-
rm -rf "$temp_dir"
|
|
104
|
-
fi
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
# Check GitHub capabilities (sub-issues extension)
|
|
108
|
-
check_github_capabilities() {
|
|
109
|
-
log_function_entry "check_github_capabilities"
|
|
110
|
-
|
|
111
|
-
if gh extension list | grep -q "yahsan2/gh-sub-issue"; then
|
|
112
|
-
use_subissues=true
|
|
113
|
-
log_info "gh-sub-issue extension detected - using sub-issues"
|
|
114
|
-
else
|
|
115
|
-
use_subissues=false
|
|
116
|
-
log_warning "gh-sub-issue extension not available - using regular issues"
|
|
117
|
-
fi
|
|
118
|
-
|
|
119
|
-
log_function_exit "check_github_capabilities"
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
# Create tasks sequentially (for small batches)
|
|
123
|
-
create_tasks_sequential() {
|
|
124
|
-
local task_files=("$@")
|
|
125
|
-
|
|
126
|
-
log_function_entry "create_tasks_sequential" "${#task_files[@]} tasks"
|
|
127
|
-
|
|
128
|
-
local labels="task,epic:$EPIC_NAME"
|
|
129
|
-
local processed=0
|
|
130
|
-
|
|
131
|
-
for task_file in "${task_files[@]}"; do
|
|
132
|
-
[[ -f "$task_file" ]] || continue
|
|
133
|
-
|
|
134
|
-
log_info "Processing task file: $(basename "$task_file")"
|
|
135
|
-
|
|
136
|
-
# Extract task name from frontmatter
|
|
137
|
-
local task_name
|
|
138
|
-
task_name=$(get_frontmatter_field "$task_file" "name")
|
|
139
|
-
|
|
140
|
-
if [[ -z "$task_name" ]]; then
|
|
141
|
-
log_error "No task name found in $task_file"
|
|
142
|
-
continue
|
|
143
|
-
fi
|
|
144
|
-
|
|
145
|
-
# Strip frontmatter for issue body
|
|
146
|
-
local task_body_file="$temp_dir/task-body-$(basename "$task_file")"
|
|
147
|
-
strip_frontmatter "$task_file" "$task_body_file"
|
|
148
|
-
|
|
149
|
-
# Create issue
|
|
150
|
-
local task_number
|
|
151
|
-
if [[ "$use_subissues" == "true" ]]; then
|
|
152
|
-
task_number=$(create_github_subissue "$EPIC_NUMBER" "$task_name" "$task_body_file" "$labels")
|
|
153
|
-
else
|
|
154
|
-
task_number=$(create_github_issue "$task_name" "$task_body_file" "$labels")
|
|
155
|
-
fi
|
|
11
|
+
if [[ -z "$EPIC_NAME" ]] || [[ -z "$EPIC_NUMBER" ]]; then
|
|
12
|
+
echo "❌ Error: Epic name and epic number required"
|
|
13
|
+
echo "Usage: $0 <epic_name> <epic_number>"
|
|
14
|
+
exit 1
|
|
15
|
+
fi
|
|
156
16
|
|
|
157
|
-
|
|
158
|
-
# Record mapping
|
|
159
|
-
echo "$task_file:$task_number" >> "$mapping_file"
|
|
160
|
-
processed=$((processed + 1))
|
|
161
|
-
log_success "Created task issue #$task_number: $task_name"
|
|
162
|
-
else
|
|
163
|
-
log_error "Failed to create issue for $task_file"
|
|
164
|
-
fi
|
|
165
|
-
done
|
|
17
|
+
EPIC_DIR=".claude/epics/$EPIC_NAME"
|
|
166
18
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
19
|
+
if [[ ! -d "$EPIC_DIR" ]]; then
|
|
20
|
+
echo "❌ Error: Epic directory not found: $EPIC_DIR"
|
|
21
|
+
exit 1
|
|
22
|
+
fi
|
|
170
23
|
|
|
171
|
-
#
|
|
172
|
-
|
|
173
|
-
|
|
24
|
+
# Mapping file - PERSISTENT location (not in /tmp)
|
|
25
|
+
MAPPING_FILE="$EPIC_DIR/.task-mapping.txt"
|
|
26
|
+
> "$MAPPING_FILE" # Clear/create file
|
|
174
27
|
|
|
175
|
-
|
|
28
|
+
echo "📋 Creating task issues for epic #$EPIC_NUMBER"
|
|
176
29
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
local batch_count=0
|
|
30
|
+
# Find all task files (sequential numbered files)
|
|
31
|
+
task_files=$(find "$EPIC_DIR" -name "[0-9]*.md" -type f | sort)
|
|
180
32
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
batch_count=$((batch_count + 1))
|
|
186
|
-
done
|
|
33
|
+
if [[ -z "$task_files" ]]; then
|
|
34
|
+
echo "❌ Error: No task files found in $EPIC_DIR"
|
|
35
|
+
exit 1
|
|
36
|
+
fi
|
|
187
37
|
|
|
188
|
-
|
|
38
|
+
task_count=$(echo "$task_files" | wc -l)
|
|
39
|
+
echo " Found $task_count tasks to create"
|
|
189
40
|
|
|
190
|
-
|
|
191
|
-
local pids=()
|
|
192
|
-
local batch_mapping_files=()
|
|
41
|
+
current=0
|
|
193
42
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
43
|
+
# Create issues for each task
|
|
44
|
+
for task_file in $task_files; do
|
|
45
|
+
((current++))
|
|
197
46
|
|
|
198
|
-
|
|
199
|
-
(
|
|
200
|
-
log_info "Starting batch $((b+1))/$batch_count"
|
|
201
|
-
process_task_batch "$b" "$batch_mapping" <<< "${batches[$b]}"
|
|
202
|
-
) &
|
|
47
|
+
task_basename=$(basename "$task_file" .md)
|
|
203
48
|
|
|
204
|
-
|
|
205
|
-
|
|
49
|
+
# Strip frontmatter and get content
|
|
50
|
+
task_content=$(awk 'BEGIN{p=0} /^---$/{p++; next} p==2{print}' "$task_file")
|
|
206
51
|
|
|
207
|
-
#
|
|
208
|
-
|
|
209
|
-
local failed_batches=0
|
|
52
|
+
# Extract title from first heading or use basename
|
|
53
|
+
task_title=$(echo "$task_content" | grep -m1 "^#" | sed 's/^# *//' || echo "Task $task_basename")
|
|
210
54
|
|
|
211
|
-
|
|
212
|
-
if wait "${pids[$p]}"; then
|
|
213
|
-
log_success "Batch $((p+1)) completed successfully"
|
|
214
|
-
else
|
|
215
|
-
log_error "Batch $((p+1)) failed"
|
|
216
|
-
failed_batches=$((failed_batches + 1))
|
|
217
|
-
fi
|
|
218
|
-
done
|
|
55
|
+
echo -n " [$current/$task_count] Creating issue for task $task_basename... "
|
|
219
56
|
|
|
220
|
-
#
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
cat "$batch_mapping" >> "$mapping_file"
|
|
225
|
-
fi
|
|
226
|
-
done
|
|
57
|
+
# Create task issue
|
|
58
|
+
issue_number=$(gh issue create \
|
|
59
|
+
--title "$task_title" \
|
|
60
|
+
--body "$task_content
|
|
227
61
|
|
|
228
|
-
|
|
229
|
-
|
|
62
|
+
---
|
|
63
|
+
**Task Information:**
|
|
64
|
+
- Epic: #$EPIC_NUMBER
|
|
65
|
+
- Original ID: $task_basename
|
|
66
|
+
- Created: $(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
67
|
+
" \
|
|
68
|
+
--label "task,epic:$EPIC_NAME" \
|
|
69
|
+
2>&1 | grep -oP '(?<=#)[0-9]+' | head -1)
|
|
230
70
|
|
|
231
|
-
if [[
|
|
232
|
-
|
|
233
|
-
|
|
71
|
+
if [[ -n "$issue_number" ]]; then
|
|
72
|
+
echo "#$issue_number ✓"
|
|
73
|
+
# Save mapping: old_name -> new_number
|
|
74
|
+
echo "$task_basename $issue_number" >> "$MAPPING_FILE"
|
|
234
75
|
else
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
fi
|
|
238
|
-
|
|
239
|
-
log_function_exit "create_tasks_parallel"
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
# Process a single batch of tasks
|
|
243
|
-
process_task_batch() {
|
|
244
|
-
local batch_number="$1"
|
|
245
|
-
local batch_mapping_file="$2"
|
|
246
|
-
local labels="task,epic:$EPIC_NAME"
|
|
247
|
-
|
|
248
|
-
# Read task files from stdin
|
|
249
|
-
local task_files=()
|
|
250
|
-
while IFS= read -r task_file; do
|
|
251
|
-
[[ -n "$task_file" ]] && task_files+=("$task_file")
|
|
252
|
-
done
|
|
253
|
-
|
|
254
|
-
log_debug "Batch $batch_number processing ${#task_files[@]} tasks"
|
|
255
|
-
|
|
256
|
-
> "$batch_mapping_file" # Initialize batch mapping file
|
|
257
|
-
|
|
258
|
-
for task_file in "${task_files[@]}"; do
|
|
259
|
-
[[ -f "$task_file" ]] || continue
|
|
260
|
-
|
|
261
|
-
# Extract task name
|
|
262
|
-
local task_name
|
|
263
|
-
task_name=$(get_frontmatter_field "$task_file" "name")
|
|
264
|
-
|
|
265
|
-
if [[ -z "$task_name" ]]; then
|
|
266
|
-
log_error "No task name in $task_file"
|
|
267
|
-
continue
|
|
268
|
-
fi
|
|
269
|
-
|
|
270
|
-
# Strip frontmatter
|
|
271
|
-
local task_body_file="$temp_dir/batch-$batch_number-$(basename "$task_file")"
|
|
272
|
-
strip_frontmatter "$task_file" "$task_body_file"
|
|
273
|
-
|
|
274
|
-
# Create issue
|
|
275
|
-
local task_number
|
|
276
|
-
if [[ "$use_subissues" == "true" ]]; then
|
|
277
|
-
task_number=$(create_github_subissue "$EPIC_NUMBER" "$task_name" "$task_body_file" "$labels")
|
|
278
|
-
else
|
|
279
|
-
task_number=$(create_github_issue "$task_name" "$task_body_file" "$labels")
|
|
280
|
-
fi
|
|
281
|
-
|
|
282
|
-
if [[ -n "$task_number" ]]; then
|
|
283
|
-
echo "$task_file:$task_number" >> "$batch_mapping_file"
|
|
284
|
-
log_debug "Batch $batch_number: Created #$task_number for $(basename "$task_file")"
|
|
285
|
-
else
|
|
286
|
-
log_error "Batch $batch_number: Failed to create issue for $(basename "$task_file")"
|
|
287
|
-
fi
|
|
288
|
-
done
|
|
289
|
-
|
|
290
|
-
log_debug "Batch $batch_number completed"
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
# Display creation results
|
|
294
|
-
display_results() {
|
|
295
|
-
local task_count="$1"
|
|
296
|
-
local created_count
|
|
297
|
-
created_count=$(wc -l < "$mapping_file" 2>/dev/null || echo 0)
|
|
298
|
-
|
|
299
|
-
print_section "✅ Task Issues Creation Results"
|
|
300
|
-
|
|
301
|
-
echo "Epic: $EPIC_NAME (Issue #$EPIC_NUMBER)"
|
|
302
|
-
echo "Strategy: $([[ $task_count -lt $PARALLEL_THRESHOLD ]] && echo "Sequential" || echo "Parallel")"
|
|
303
|
-
echo "Sub-issues: $([[ "$use_subissues" == "true" ]] && echo "Enabled" || echo "Disabled")"
|
|
304
|
-
echo "Created: $created_count/$task_count task issues"
|
|
305
|
-
|
|
306
|
-
if [[ $created_count -gt 0 ]]; then
|
|
307
|
-
echo ""
|
|
308
|
-
echo "Task mappings:"
|
|
309
|
-
while IFS=: read -r task_file issue_number; do
|
|
310
|
-
local task_name
|
|
311
|
-
task_name=$(basename "$task_file" .md)
|
|
312
|
-
echo " #$issue_number: $task_name"
|
|
313
|
-
done < "$mapping_file"
|
|
314
|
-
fi
|
|
315
|
-
|
|
316
|
-
if [[ $created_count -lt $task_count ]]; then
|
|
317
|
-
echo ""
|
|
318
|
-
echo "⚠️ Some issues failed to create. Check logs for details."
|
|
76
|
+
echo "FAILED"
|
|
77
|
+
echo "⚠️ Failed to create issue for $task_basename"
|
|
319
78
|
fi
|
|
320
|
-
|
|
79
|
+
done
|
|
321
80
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
log_error "Script failed with exit code: $exit_code"
|
|
326
|
-
log_error "Task issues creation failed for epic: $EPIC_NAME"
|
|
327
|
-
exit "$exit_code"
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
# Set up error handling
|
|
331
|
-
trap handle_error ERR
|
|
332
|
-
|
|
333
|
-
# Validate arguments
|
|
334
|
-
if [[ $# -lt 2 ]]; then
|
|
335
|
-
echo "Usage: $0 <epic_name> <epic_issue_number>"
|
|
336
|
-
echo ""
|
|
337
|
-
echo "Creates GitHub issues for all tasks in the specified epic."
|
|
338
|
-
echo ""
|
|
339
|
-
echo "Arguments:"
|
|
340
|
-
echo " epic_name Name of the epic directory"
|
|
341
|
-
echo " epic_issue_number GitHub issue number of the parent epic"
|
|
342
|
-
echo ""
|
|
343
|
-
echo "Environment Variables:"
|
|
344
|
-
echo " AUTOPM_PARALLEL_THRESHOLD Minimum tasks for parallel execution (default: 5)"
|
|
345
|
-
echo ""
|
|
346
|
-
echo "Examples:"
|
|
347
|
-
echo " $0 authentication 123"
|
|
348
|
-
echo " AUTOPM_PARALLEL_THRESHOLD=3 $0 user-dashboard 456"
|
|
349
|
-
echo ""
|
|
350
|
-
exit 1
|
|
351
|
-
fi
|
|
81
|
+
echo ""
|
|
82
|
+
echo "✅ Created $current task issues"
|
|
83
|
+
echo " Mapping saved to: $MAPPING_FILE"
|
|
352
84
|
|
|
353
|
-
#
|
|
354
|
-
|
|
85
|
+
# Output the mapping file path for next script
|
|
86
|
+
echo "$MAPPING_FILE"
|