Files
vault-cli/vault-topology.py
Hermes Agent 48681b00c5 vault-cli: Librarian tooling for the R/J/F/I vault
Adds packages/vault-cli (the `vault` CLI: search/show/tree, clobber-safe
create/move/rename across both /vault and /data + manifest regen, sync-data,
report, analyze) and wires it onto $PATH via modules/hosts/core/vault.nix for
server + vault-client hosts. Scripts self-locate siblings via realpath so they
run from the Nix store. Includes the deterministic $0 report generator, the
topology-manifest generator, and the shared Librarian prompt.
2026-07-22 11:11:03 -04:00

72 lines
2.9 KiB
Python

#!/usr/bin/env python3
"""Generate/refresh the vault topology manifest note (source of truth for both trees)."""
import os, json, sys, datetime
sys.path.insert(0, os.path.dirname(os.path.realpath(__file__)))
import vault_lib as vl
def render(manifest):
ts = datetime.date.today().isoformat()
c = manifest["counts"]
lines = []
lines.append("---")
lines.append("tags: [meta, topology, jdex]")
lines.append("generated: " + ts)
lines.append("---")
lines.append("# 00.05 Vault topology (machine-readable)")
lines.append("")
lines.append("> [!info] Source of truth")
lines.append("> This note is the **authoritative topology** for the R/J/F/I vault. It is")
lines.append("> regenerated from the live `/vault` structure by `vault-topology.py` and is")
lines.append("> what the `vault` CLI and the nightly Librarian reconcile **both** `/vault`")
lines.append("> (the JDex/map) and `/data` (blob territory) against. Do not hand-edit the")
lines.append("> JSON block below — change the vault via the `vault` CLI and regenerate.")
lines.append("")
lines.append(f"**Live counts:** {c['systems']} systems · {c['areas']} areas · "
f"{c['categories']} categories · {c['ids']} IDs")
lines.append("")
lines.append("## Systems")
for k, v in manifest["systems"].items():
lines.append(f"- **{k}** — {v}")
lines.append("")
lines.append("## Categories by system")
# group categories under systems
by_sys = {}
for code, name in manifest["categories"].items():
by_sys.setdefault(code[0], []).append((code, name))
for s in sorted(by_sys):
lines.append(f"### {s}")
for code, name in sorted(by_sys[s]):
n_ids = sum(1 for idc in manifest["ids"] if idc.startswith(code + "."))
lines.append(f"- `{code}` {name.split(' ',1)[1] if ' ' in name else name} — {n_ids} IDs")
lines.append("")
lines.append("## Machine-readable topology")
lines.append("")
lines.append(vl.MANIFEST_BEGIN)
lines.append("```json")
lines.append(json.dumps(manifest, indent=2, ensure_ascii=False))
lines.append("```")
lines.append(vl.MANIFEST_END)
lines.append("")
return "\n".join(lines)
def main():
manifest = vl.build_manifest()
content = render(manifest)
dest = os.path.join(vl.VAULT, vl.MANIFEST_REL)
os.makedirs(os.path.dirname(dest), exist_ok=True)
dry = "--execute" not in sys.argv
if dry:
print("DRY-RUN. Would write manifest to:")
print(" " + dest)
print(f" ({manifest['counts']})")
print("--- preview (first 40 lines) ---")
print("\n".join(content.splitlines()[:40]))
else:
with open(dest, "w", encoding="utf-8") as f:
f.write(content)
print("WROTE " + dest)
print(f" {manifest['counts']}")
if __name__ == "__main__":
main()