# tools/restructure_imports.py """One-shot codemod: rewrite proteus import paths for the game/web restructure. Usage: python tools/restructure_imports.py [ ...] NEVER run this on tools/ — this file contains the rule strings themselves. Rules apply IN ORDER (most-specific first) so prefix collisions resolve correctly: * grid.scenarios (plural) before grid.scenario (singular) before grid (bare) * runtime.metrics/persona/rollout/aggregate before runtime (bare) Each rule matches a dotted path at a word boundary so 'proteus.grid' does not hit 'proteus.gridx', and (because longer rules run first) does not pre-empt 'proteus.grid.game'. """ from __future__ import annotations import re import sys from pathlib import Path RULES: list[tuple[str, str]] = [ # grid family — plural, then singular, then submodules, then bare ("proteus.grid.scenarios", "proteus.game.scenarios"), ("proteus.grid.scenario", "proteus.game.scenarios.base"), ("proteus.grid.game", "proteus.game.engine.grid"), ("proteus.grid.difficulty", "proteus.game.engine.difficulty"), ("proteus.grid.ascii_view", "proteus.game.engine.ascii_view"), ("proteus.grid", "proteus.game.scenarios"), # arc_grid (engine + rendering) ("proteus.arc_grid", "proteus.game.engine"), # runtime — metrics group out to game.metrics (specific first), then bare ("proteus.runtime.metrics", "proteus.game.metrics.metrics"), ("proteus.runtime.persona", "proteus.game.metrics.persona"), ("proteus.runtime.rollout", "proteus.game.metrics.rollout"), ("proteus.runtime.aggregate", "proteus.game.metrics.aggregate"), ("proteus.runtime", "proteus.game.runtime"), # agents / viz ("proteus.agents", "proteus.game.agents"), ("proteus.viz", "proteus.game.viz"), # web -> web.local (arena is new; no old refs) ("proteus.web", "proteus.web.local"), ] COMPILED = [(re.compile(r"\b" + re.escape(old) + r"(?![\w])"), new) for old, new in RULES] def rewrite(text: str) -> str: for pat, new in COMPILED: text = pat.sub(new, text) return text def iter_py(paths: list[str]): for p in paths: path = Path(p) if path.is_dir(): yield from sorted(path.rglob("*.py")) elif path.suffix == ".py": yield path def main(argv: list[str]) -> int: changed = 0 for path in iter_py(argv): old = path.read_text() new = rewrite(old) if new != old: path.write_text(new) changed += 1 print(f"rewrote {path}") print(f"--- {changed} files changed ---") return 0 if __name__ == "__main__": raise SystemExit(main(sys.argv[1:]))