"""CLI for backend migrations. Usage: python -m src.lib.system.migrate inventory --to cloud # merge (default) python -m src.lib.system.migrate inventory --to cloud --mode replace # destructive python -m src.lib.system.migrate vectors --to cloud python -m src.lib.system.migrate vectors --to local --mode replace Modes: merge (default, safe) Add new rows/points; existing target rows untouched. Idempotent — re-running produces the same end state. replace (destructive) Wipe target completely, then copy from source. """ from __future__ import annotations import argparse from .migration import migrate_inventory, migrate_vectors def main(argv: list[str] | None = None) -> int: ap = argparse.ArgumentParser(description="Migrate data between local and cloud backends") sub = ap.add_subparsers(dest="component", required=True) inv = sub.add_parser("inventory", help="Migrate the inventory DB (SQLite <-> Turso)") inv.add_argument("--to", required=True, choices=("local", "cloud")) inv.add_argument("--mode", default="merge", choices=("merge", "replace")) vec = sub.add_parser("vectors", help="Migrate Qdrant points (local <-> cloud)") vec.add_argument("--to", required=True, choices=("local", "cloud")) vec.add_argument("--collection", default=None, help="defaults to the active collection") vec.add_argument("--mode", default="merge", choices=("merge", "replace")) args = ap.parse_args(argv) direction = "local_to_cloud" if args.to == "cloud" else "cloud_to_local" if args.component == "inventory": report = migrate_inventory(direction, mode=args.mode) print(f"Inventory migration ({report.mode}) {direction}: " f"{report.total_rows} rows in {report.elapsed_ms} ms") for table, n in report.rows_per_table.items(): print(f" {table:20s} {n:>6}") if report.skipped_tables: print(f" skipped (log tables in merge mode): {', '.join(report.skipped_tables)}") else: report = migrate_vectors(direction, collection=args.collection, mode=args.mode) print(f"Vector migration ({report.mode}) {direction}: " f"collection {report.collection!r}, " f"{report.points} points in {report.elapsed_ms} ms") return 0 if __name__ == "__main__": raise SystemExit(main())