port_manifest.py 1.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152
  1. from __future__ import annotations
  2. from collections import Counter
  3. from dataclasses import dataclass
  4. from pathlib import Path
  5. from .models import Subsystem
  6. DEFAULT_SRC_ROOT = Path(__file__).resolve().parent
  7. @dataclass(frozen=True)
  8. class PortManifest:
  9. src_root: Path
  10. total_python_files: int
  11. top_level_modules: tuple[Subsystem, ...]
  12. def to_markdown(self) -> str:
  13. lines = [
  14. f'Port root: `{self.src_root}`',
  15. f'Total Python files: **{self.total_python_files}**',
  16. '',
  17. 'Top-level Python modules:',
  18. ]
  19. for module in self.top_level_modules:
  20. lines.append(f'- `{module.name}` ({module.file_count} files) — {module.notes}')
  21. return '\n'.join(lines)
  22. def build_port_manifest(src_root: Path | None = None) -> PortManifest:
  23. root = src_root or DEFAULT_SRC_ROOT
  24. files = [path for path in root.rglob('*.py') if path.is_file()]
  25. counter = Counter(
  26. path.relative_to(root).parts[0] if len(path.relative_to(root).parts) > 1 else path.name
  27. for path in files
  28. if path.name != '__pycache__'
  29. )
  30. notes = {
  31. '__init__.py': 'package export surface',
  32. 'main.py': 'CLI entrypoint',
  33. 'port_manifest.py': 'workspace manifest generation',
  34. 'query_engine.py': 'port orchestration summary layer',
  35. 'commands.py': 'command backlog metadata',
  36. 'tools.py': 'tool backlog metadata',
  37. 'models.py': 'shared dataclasses',
  38. 'task.py': 'task-level planning structures',
  39. }
  40. modules = tuple(
  41. Subsystem(name=name, path=f'src/{name}', file_count=count, notes=notes.get(name, 'Python port support module'))
  42. for name, count in counter.most_common()
  43. )
  44. return PortManifest(src_root=root, total_python_files=len(files), top_level_modules=modules)