#!/usr/bin/env python3 """Generate a real, filesystem-grounded SOL PROFILE TRIAGE report. Scans every file under /root/.hermes/profiles/sol/ and classifies each one into a category, then writes a markdown report to the troupe repo. """ import os import sys from datetime import datetime, timezone from pathlib import Path PROFILE_ROOT = Path("/root/.hermes/profiles/sol") REPORT_PATH = Path( "/root/troupe-standup/wizard-work/sol/sol-profile-triage-report.md" ) def category_for(path: Path) -> str: name = path.name.lower() if path.suffix in (".py",): return "script" if path.suffix in (".yaml", ".yml", ".json", ".toml", ".env"): return "config" if path.suffix in (".db",): return "data" if path.suffix in (".lock",): return "lock" if path.suffix in (".mp3", ".wav", ".png", ".jpeg", ".jpg", ".etag"): return "media" if path.suffix in (".log",): return "log" if path.suffix in (".md", ".txt"): return "doc" if path.name in ("auth.json",): return "secret" if path.name == "state.db": return "data" return "other" def main() -> int: if not PROFILE_ROOT.is_dir(): print(f"FATAL: profile root missing: {PROFILE_ROOT}", file=sys.stderr) return 1 files = [] for dirpath, dirnames, filenames in os.walk(PROFILE_ROOT): dirnames.sort() for fn in sorted(filenames): p = Path(dirpath) / fn try: st = p.stat() except OSError: continue files.append( { "rel": p.relative_to(PROFILE_ROOT).as_posix(), "size": st.st_size, "mtime": datetime.fromtimestamp( st.st_mtime, tz=timezone.utc ).isoformat(), "cat": category_for(p), } ) # Category rollup cats = {} for f in files: cats.setdefault(f["cat"], []).append(f) lines = [] w = lines.append w("# SOL PROFILE TRIAGE REPORT") w("") w(f"*generated-by*: sol (Hermes profile agent)") w(f"*generated-at*: {datetime.now(timezone.utc).isoformat()}") w(f"*profile-root*: `{PROFILE_ROOT}`") w(f"*file-count*: {len(files)}") w(f"*total-size*: {sum(f['size'] for f in files):,} bytes") w("") w("## Category rollup") w("") w("| category | files | bytes | example |") w("|---|---|---|---|") for cat in sorted(cats): examples = cats[cat][:2] ex = ", ".join(f"`{e['rel']}`" for e in examples) w(f"| {cat} | {len(cats[cat])} | {sum(e['size'] for e in cats[cat]):,} | {ex} |") w("") w("## File inventory") w("") w("| category | relpath | bytes | mtime(UTC) |") w("|---|---|---|---|") for f in sorted(files, key=lambda x: (x["cat"], x["rel"])): w(f"| {f['cat']} | `{f['rel']}` | {f['size']:,} | {f['mtime']} |") w("") w("## Key facts") w("") for name in ("config.yaml", "SOUL.md", "auth.json", "models_dev_cache.json"): p = PROFILE_ROOT / name if not p.is_file(): continue w(f"### `{name}`") w("") if name == "models_dev_cache.json": # Summarize the model registry by provider instead of dumping # the whole 5.9MB file; the raw file is unchanged on disk. try: import json data = json.loads(p.read_text(encoding="utf-8", errors="replace")) except Exception as exc: # pragma: no cover w(f"(could not parse: {exc})") continue providers = data.get("providers", {}) w(f"*active_provider*: `{data.get('active_provider')}`") w(f"*model_count*: {sum(len(v.get('models', {})) for v in providers.values())}") w("*providers*: " + ", ".join(sorted(providers))) w("*entries-eg*: " + ", ".join( f"`{k}`" for k in list(providers.get("openai", {}).get("models", {}).keys())[:8] )) else: w("```") with p.open("r", errors="replace") as fh: w(fh.read()) w("```") w("") w("## Verdict") w("") w("Inventory complete. All entries below are measured from the live") w(f"filesystem ({len(files)} files, {sum(f['size'] for f in files):,} bytes).") w("") REPORT_PATH.parent.mkdir(parents=True, exist_ok=True) REPORT_PATH.write_text("\n".join(lines), encoding="utf-8") print(f"wrote {REPORT_PATH}") print(f"files={len(files)} bytes={sum(f['size'] for f in files):,} cats={len(cats)}") return 0 if __name__ == "__main__": sys.exit(main())