#!/usr/bin/env python3 """ Reorganización del monorepo brahman (2026-05-19). Pasos: 1. git mv directorios según MOVES 2. Renombra package names en Cargo.toml según PKG_RENAMES 3. Reescribe paths relativos (path = "...") en TODOS los Cargo.toml 4. Reescribe imports `use OLD_::` → `use NEW_::` en .rs 5. Reescribe root Cargo.toml (members + workspace.dependencies) Renames: shipote → shuma nouser → akasha yahweh → nahual (carpeta ui_engine también renombrada) lapaloma → pineal (promovida fuera de ui_engine) Uso: python3 scripts/reorg.py --dry-run python3 scripts/reorg.py """ import os, re, subprocess, sys from pathlib import Path ROOT = Path("/home/sergio/brahman") DRY = "--dry-run" in sys.argv # ============================================================ # 1) MOVES: old_canonical_dir → new_canonical_dir (relativo a ROOT) # ============================================================ MOVES: dict[str, str] = {} PROTOCOL = ["brahman-card", "brahman-card-wit", "brahman-cards", "brahman-handshake", "brahman-broker", "brahman-admin", "ente-card"] INIT_C = ["ente-zero", "ente-kernel", "ente-soma", "ente-snapshot"] RUNTIME = ["ente-bus", "ente-cas", "ente-wasm", "ente-brain", "ente-echo"] COMPAT = ["ente-logind-compat", "ente-hostnamed-compat", "ente-timedated-compat", "ente-localed-compat", "ente-journald-compat", "ente-resolved-compat", "ente-polkit-compat", "ente-machined-compat", "ente-systemd1-compat", "ente-notify-compat", "ente-timer-compat", "ente-tmpfiles-compat", "ente-binfmt-compat", "ente-policy-provider"] for c in PROTOCOL: MOVES[f"crates/core/{c}"] = f"crates/protocol/{c}" for c in INIT_C: MOVES[f"crates/core/{c}"] = f"crates/init/{c}" for c in RUNTIME: MOVES[f"crates/core/{c}"] = f"crates/runtime/{c}" for c in COMPAT: MOVES[f"crates/core/{c}"] = f"crates/compat/{c}" MOVES["crates/shared/brahman-sidecar"] = "crates/protocol/brahman-sidecar" MOVES["crates/shared/brahman-net"] = "crates/protocol/brahman-net" MOVES["crates/shared/ente-incarnate"] = "crates/init/ente-incarnate" # shipote → shuma for c in ["card", "core", "protocol", "discern"]: MOVES[f"crates/modules/shipote/shipote-{c}"] = f"crates/modules/shuma/shuma-{c}" for c in ["cli", "daemon", "shell", "gateway"]: MOVES[f"crates/apps/shipote-{c}"] = f"crates/apps/shuma-{c}" # nouser → akasha (mantiene naming interno sin prefijo) for c in ["card", "core", "nous", "nous-mock", "nous-real"]: MOVES[f"crates/modules/nouser/{c}"] = f"crates/modules/akasha/{c}" MOVES["crates/apps/nouser-explorer"] = "crates/apps/akasha-explorer" # ui_engine → nahual (libs/* + widgets/* salvo lapaloma-*) NAHUAL_LIBS = ["core", "theme", "launcher", "bus", "meta-schema", "meta-runtime", "providers/fs", "providers/sqlite"] for c in NAHUAL_LIBS: MOVES[f"crates/modules/ui_engine/libs/{c}"] = f"crates/modules/nahual/libs/{c}" NAHUAL_WIDGETS = ["tree", "container_core", "splitter", "tabs", "tiled", "text_input", "meta-form", "banner", "card", "stat-card", "app-header", "theme-switcher"] for c in NAHUAL_WIDGETS: MOVES[f"crates/modules/ui_engine/widgets/{c}"] = f"crates/modules/nahual/widgets/{c}" # lapaloma → pineal (promovida fuera de ui_engine) MOVES["crates/modules/ui_engine/libs/lapaloma-core"] = "crates/modules/pineal/core" PINEAL_W = ["render", "cartesian", "stream", "mesh", "financial", "polar", "heatmap", "treemap", "flow", "phosphor", "export"] for c in PINEAL_W: MOVES[f"crates/modules/ui_engine/widgets/lapaloma-{c}"] = f"crates/modules/pineal/{c}" MOVES["crates/modules/ui_engine/widgets/lapaloma"] = "crates/modules/pineal/umbrella" # Apps: renames de yahweh, lapaloma MOVES["crates/apps/yahweh-shell"] = "crates/apps/nahual-shell" MOVES["crates/apps/file_explorer"] = "crates/apps/nahual-file-explorer" MOVES["crates/apps/database_explorer"] = "crates/apps/nahual-database-explorer" MOVES["crates/apps/text_viewer"] = "crates/apps/nahual-text-viewer" MOVES["crates/apps/image_viewer"] = "crates/apps/nahual-image-viewer" for c in ["demo", "stream-demo", "phosphor-demo", "financial-demo"]: MOVES[f"crates/apps/lapaloma-{c}"] = f"crates/apps/pineal-{c}" # ============================================================ # 2) PKG_RENAMES: nombre paquete viejo → nombre paquete nuevo # ============================================================ PKG_RENAMES: dict[str, str] = {} for s in ["card", "core", "protocol", "discern", "cli", "daemon", "shell", "gateway"]: PKG_RENAMES[f"shipote-{s}"] = f"shuma-{s}" for s in ["card", "core", "nous", "nous-mock", "nous-real", "explorer"]: PKG_RENAMES[f"nouser-{s}"] = f"akasha-{s}" YAHWEH = [ "core", "theme", "launcher", "bus", "meta-schema", "meta-runtime", "provider-fs", "provider-sqlite", "widget-tree", "widget-container-core", "widget-splitter", "widget-tabs", "widget-tiled", "widget-text-input", "widget-meta-form", "widget-banner", "widget-card", "widget-stat-card", "widget-app-header", "widget-theme-switcher", "shell", "file-explorer", "database-explorer", "text-viewer", "image-viewer", ] for s in YAHWEH: PKG_RENAMES[f"yahweh-{s}"] = f"nahual-{s}" LAPAL = ["core", "render", "cartesian", "stream", "mesh", "financial", "polar", "heatmap", "treemap", "flow", "phosphor", "export", "demo", "stream-demo", "phosphor-demo", "financial-demo"] for s in LAPAL: PKG_RENAMES[f"lapaloma-{s}"] = f"pineal-{s}" PKG_RENAMES["lapaloma"] = "pineal" # Bare names (binarios umbrella que se llaman como el proyecto) PKG_RENAMES["shipote"] = "shuma" PKG_RENAMES["yahweh"] = "nahual" PKG_RENAMES["nouser"] = "akasha" # ============================================================ # 3) Helpers # ============================================================ def sh(cmd, check=True): if DRY: print(f" DRY: {' '.join(cmd)}") return None return subprocess.run(cmd, check=check, cwd=str(ROOT)) def git_mv(src: str, dst: str): src_p = ROOT / src dst_p = ROOT / dst if not src_p.exists(): print(f" SKIP {src} (no existe)") return if dst_p.exists(): print(f" WARN {dst} ya existe — saltando") return dst_p.parent.mkdir(parents=True, exist_ok=True) sh(["git", "mv", src, dst]) def all_cargo_tomls() -> list[Path]: res = [] for p in (ROOT / "crates").rglob("Cargo.toml"): if "target" in p.parts: continue res.append(p) return res def all_rs() -> list[Path]: res = [] for p in (ROOT / "crates").rglob("*.rs"): if "target" in p.parts: continue res.append(p) return res # ============================================================ # 4) Reescritura de paths relativos en Cargo.toml # ============================================================ # Después de los git mv, owner está en su NEW path. El path = "..." # dentro del file aún tiene el valor viejo (relativo al old owner). # Estrategia: probamos resolver el path desde el OLD owner; si el # target existe en MOVES o en disco, lo redirigimos al NEW target # y recomputamos relativo al NEW owner. CURRENT_TO_OLD = {new: old for old, new in MOVES.items()} def owner_old_for(toml: Path) -> str: cur = str(toml.parent.relative_to(ROOT)) return CURRENT_TO_OLD.get(cur, cur) def resolve_canon(base_dir: str, rel: str) -> str | None: try: abs_t = (ROOT / base_dir / rel).resolve() return str(abs_t.relative_to(ROOT)) except ValueError: return None def rewrite_paths_in_toml(toml: Path): text = toml.read_text() owner_old = owner_old_for(toml) owner_new = MOVES.get(owner_old, owner_old) def repl(m: re.Match) -> str: rel_in_file = m.group(1) # Try resolve from owner_old first (file values are pre-move). # Si owner no se movió, owner_old == owner_new — sigue válido. for base in (owner_old, owner_new): tgt = resolve_canon(base, rel_in_file) if tgt is None: continue # ¿Conocemos un MOVES para este target? if tgt in MOVES: new_tgt = MOVES[tgt] elif (ROOT / tgt).exists(): new_tgt = tgt else: continue new_rel = os.path.relpath(str(ROOT / new_tgt), str(ROOT / owner_new)) return f'path = "{new_rel}"' return m.group(0) new_text = re.sub(r'path\s*=\s*"([^"]+)"', repl, text) if new_text != text: if DRY: print(f" DRY paths: {toml.relative_to(ROOT)}") else: toml.write_text(new_text) # ============================================================ # 5) Reescritura de package names en Cargo.toml # ============================================================ def rewrite_pkg_names_in_toml(toml: Path): text = toml.read_text() orig = text # Ordenamos de más largo a más corto para evitar que `shipote` reemplace # antes que `shipote-core` cuando este último también está en el map. for old, new in sorted(PKG_RENAMES.items(), key=lambda kv: -len(kv[0])): # name = "OLD" / package = "OLD" text = re.sub(rf'(\bname\s*=\s*)"{re.escape(old)}"', rf'\1"{new}"', text) text = re.sub(rf'(\bpackage\s*=\s*)"{re.escape(old)}"', rf'\1"{new}"', text) # Dep key al inicio de línea: OLD = ... text = re.sub(rf'^([ \t]*){re.escape(old)}(\s*=)', rf'\1{new}\2', text, flags=re.MULTILINE) # Features y strings: "dep:OLD" → "dep:NEW" text = re.sub(rf'"dep:{re.escape(old)}"', f'"dep:{new}"', text) # Features: "OLD/feature" → "NEW/feature" text = re.sub(rf'"{re.escape(old)}/', f'"{new}/', text) # Features: "OLD" → "NEW" (referencia bare en feature list) text = re.sub(rf'"{re.escape(old)}"', f'"{new}"', text) if text != orig: if DRY: print(f" DRY names: {toml.relative_to(ROOT)}") else: toml.write_text(text) # ============================================================ # 6) Reescritura de imports en .rs # ============================================================ USE_RENAMES = {k.replace("-", "_"): v.replace("-", "_") for k, v in PKG_RENAMES.items()} def rewrite_imports_in_rs(rs: Path): text = rs.read_text() orig = text for old, new in USE_RENAMES.items(): text = re.sub(rf'(?