Orchestrates the full split workflow.
| 1506 | # ─── Main Splitter Orchestrator ─────────────────────────────────────────────── |
| 1507 | |
| 1508 | class Splitter: |
| 1509 | """Orchestrates the full split workflow.""" |
| 1510 | |
| 1511 | def __init__( |
| 1512 | self, |
| 1513 | source_path: Path, |
| 1514 | output_dir: Path, |
| 1515 | layout: Dict[str, List[str]], |
| 1516 | dry_run: bool = False, |
| 1517 | update_mode: bool = False, |
| 1518 | show_tree: bool = False, |
| 1519 | dump_layout: bool = False, |
| 1520 | report_name: str = "FRAMEWORK.md", |
| 1521 | generate_report: bool = True, |
| 1522 | ) -> None: |
| 1523 | self.source_path = source_path |
| 1524 | self.output_dir = output_dir |
| 1525 | self.layout = layout |
| 1526 | self.dry_run = dry_run |
| 1527 | self.update_mode = update_mode |
| 1528 | self.show_tree = show_tree |
| 1529 | self.dump_layout_flag = dump_layout |
| 1530 | self.report_name = str(report_name or "FRAMEWORK.md").strip() or "FRAMEWORK.md" |
| 1531 | self.generate_report = bool(generate_report) |
| 1532 | self.package_name = source_path.stem.lower().replace(" ", "_").replace("-", "_") |
| 1533 | |
| 1534 | def run(self) -> None: |
| 1535 | print(f"\n{'─'*60}") |
| 1536 | print(f" split_coder.py — splitting {self.source_path.name}") |
| 1537 | print(f" Output: {self.output_dir}") |
| 1538 | print(f"{'─'*60}\n") |
| 1539 | |
| 1540 | # ── Step 1: Analyze source ───────────────────────────────────────── |
| 1541 | print("[1/7] Analyzing source architecture...") |
| 1542 | analyzer = ArchitectureAnalyzer(self.source_path) |
| 1543 | nodes = analyzer.analyze() |
| 1544 | print(f" Found {len(nodes)} top-level nodes") |
| 1545 | |
| 1546 | # ── Step 2: Load existing manifest (for update mode) ─────────────── |
| 1547 | old_manifest = SplitManifest.load(self.output_dir) if self.update_mode else None |
| 1548 | if self.update_mode and old_manifest: |
| 1549 | print(f"[2/7] Loaded existing manifest ({len(old_manifest.nodes)} nodes)") |
| 1550 | else: |
| 1551 | print("[2/7] Starting fresh split") |
| 1552 | |
| 1553 | # ── Step 3: Route nodes to modules ──────────────────────────────── |
| 1554 | print("[3/7] Routing symbols to modules...") |
| 1555 | router = ModuleRouter(self.layout) |
| 1556 | router.assign_all(nodes) |
| 1557 | # Group by target module |
| 1558 | modules: Dict[str, List[NodeInfo]] = defaultdict(list) |
| 1559 | for node in nodes: |
| 1560 | modules[node.target_module].append(node) |
| 1561 | |
| 1562 | unclassified = modules.get("_unclassified.py", []) |
| 1563 | classified_count = len(nodes) - len(unclassified) - len(modules.get("_imports", [])) |
| 1564 | print(f" Classified {classified_count} symbols into {len(modules)} modules") |
| 1565 | if unclassified: |