#!/usr/bin/env python3 """ Generate task files from plan.md This script parses the plan.md file and creates detailed task files for each task. """ import re import os from pathlib import Path def parse_plan(plan_path): """Parse plan.md and extract tasks""" with open(plan_path, 'r') as f: content = f.read() tasks = [] current_epic = None current_section = None subtask_num = 1 lines = content.split('\n') i = 0 while i < len(lines): line = lines[i].rstrip() # Epic header (supports legacy Phase headers for compatibility) epic_match = re.match(r'^## Epic (\d+):', line) legacy_phase_match = re.match(r'^## Phase (\d+):', line) if epic_match or legacy_phase_match: current_epic = int((epic_match or legacy_phase_match).group(1)) subtask_num = 1 # Reset subtask counter for new epic i += 1 continue # Section header (e.g., "#### 0.1 Repository Bootstrap") section_match = re.match(r'^#### (\d+\.\d+)', line) if section_match: current_section = section_match.group(1) subtask_num = 1 # Reset subtask counter for new section i += 1 continue # Task item (checkbox) - must match exactly task_match = re.match(r'^- \[ \] (.+)', line) if task_match and current_epic is not None and current_section is not None: task_desc = task_match.group(1).strip() # Handle tasks that end with colon (might have code block or list following) code_block = "" # Skip empty lines and code blocks if i + 1 < len(lines): next_line = lines[i + 1].strip() if next_line.startswith('```'): # Extract code block j = i + 2 while j < len(lines) and not lines[j].strip().startswith('```'): code_block += lines[j] + '\n' j += 1 i = j + 1 elif next_line.startswith('- [ ]') or next_line.startswith('```'): # Next task or code block, don't skip i += 1 else: i += 1 else: i += 1 # Only add if we have valid epic and section if current_epic is not None and current_section is not None: tasks.append({ 'epic': current_epic, 'section': current_section, 'subtask': subtask_num, 'description': task_desc, 'code': code_block.strip() }) subtask_num += 1 continue i += 1 return tasks def create_task_file(task, output_dir): """Create a task markdown file""" epic_dir = output_dir / f"epic{task['epic']}" epic_dir.mkdir(exist_ok=True) task_id = f"{task['section']}.{task['subtask']}" # Create safe filename safe_desc = re.sub(r'[^\w\s-]', '', task['description'])[:50].strip().replace(' ', '-').lower() filename = f"{task_id}-{safe_desc}.md" filepath = epic_dir / filename # Generate content content = f"""# Task {task_id}: {task['description']} ## Metadata - **Task ID**: {task_id} - **Title**: {task['description']} - **Epic**: {task['epic']} - {get_epic_name(task['epic'])} - **Section**: {task['section']} - **Status**: Pending - **Priority**: High - **Estimated Time**: TBD - **Dependencies**: TBD ## Description {task['description']} ## Requirements - {task['description']} ## Implementation Steps 1. TODO: Add implementation steps 2. TODO: Add implementation steps 3. TODO: Add implementation steps ## Acceptance Criteria - [ ] Task {task_id} is completed - [ ] All requirements are met - [ ] Code compiles and tests pass ## Related ADRs - See relevant ADRs in `docs/adr/` ## Implementation Notes - TODO: Add implementation notes ## Testing ```bash # TODO: Add test commands go test ./... ``` """ if task['code']: content += f"\n## Code Reference\n\n```go\n{task['code']}\n```\n" with open(filepath, 'w') as f: f.write(content) return filepath def get_epic_name(epic_num): """Get epic name from number""" epics = { 0: "Project Setup & Foundation", 1: "Core Kernel & Infrastructure", 2: "Authentication & Authorization", 3: "Module Framework", 4: "Sample Feature Module (Blog)", 5: "Infrastructure Adapters", 6: "Observability & Production Readiness", 7: "Testing, Documentation & CI/CD", 8: "Advanced Features & Polish" } return epics.get(epic_num, "Unknown") def main(): script_dir = Path(__file__).parent plan_path = script_dir.parent / "plan.md" output_dir = script_dir if not plan_path.exists(): print(f"Error: {plan_path} not found") return print(f"Parsing {plan_path}...") try: tasks = parse_plan(plan_path) print(f"Found {len(tasks)} tasks") if len(tasks) == 0: print("Warning: No tasks found. Check the plan.md format.") return created = 0 skipped = 0 for task in tasks: try: task_id = f"{task['section']}.{task['subtask']}" # Determine filepath before creating epic_dir = output_dir / f"epic{task['epic']}" epic_dir.mkdir(exist_ok=True) # Create safe filename safe_desc = re.sub(r'[^\w\s-]', '', task['description'])[:50].strip().replace(' ', '-').lower() filename = f"{task_id}-{safe_desc}.md" filepath = epic_dir / filename # Check if file already exists (skip if so) if filepath.exists() and filepath.stat().st_size > 100: skipped += 1 continue # Create the file create_task_file(task, output_dir) created += 1 if created % 10 == 0: print(f"Created {created} task files...") except Exception as e: print(f"Error creating task {task.get('section', '?')}.{task.get('subtask', '?')}: {e}") import traceback traceback.print_exc() print(f"\nCreated {created} new task files") if skipped > 0: print(f"Skipped {skipped} existing task files") print(f"Total tasks processed: {len(tasks)}") except Exception as e: print(f"Error: {e}") import traceback traceback.print_exc() if __name__ == '__main__': main()