"""Build a target library tree preview from an execution plan.""" from __future__ import annotations from collections import defaultdict from pathlib import Path from vlm.models import ExecutionPlan from vlm.review_display import display_path def build_structure_preview_lines( plan: ExecutionPlan, library_root: Path, *, max_titles: int = 40, max_paths_per_title: int = 8, ) -> list[str]: """Return ASCII lines showing where move/rename operations will place files.""" by_title: dict[str, list[str]] = defaultdict(list) for op in plan.operations: if op.operation_type not in ("move", "rename") or not op.destination_path: continue ctx = op.review_context if isinstance(op.review_context, dict) else {} title = str(ctx.get("title") or op.destination_path.parent.parent.name) rel = display_path(op.destination_path, library_root) by_title[title].append(rel) lines = [ "Target library structure preview (move/rename destinations)", f"Library root: {library_root}", "", ] if not by_title: lines.append("(no move/rename destinations)") return lines sorted_titles = sorted(by_title.keys())[:max_titles] for title in sorted_titles: paths = sorted(set(by_title[title]))[:max_paths_per_title] lines.append(f"{title}/") for p in paths: lines.append(f" {p}") remaining = len(by_title[title]) - len(paths) if remaining > 0: lines.append(f" ... +{remaining} more path(s)") lines.append("") if len(by_title) > max_titles: lines.append(f"... +{len(by_title) - max_titles} more title(s)") return lines def write_structure_preview( plan: ExecutionPlan, library_root: Path, output_path: Path, ) -> None: """Write structure preview text to a file.""" lines = build_structure_preview_lines(plan, library_root) output_path.parent.mkdir(parents=True, exist_ok=True) output_path.write_text("\n".join(lines) + "\n", encoding="utf-8")