#!/usr/bin/env python3 """Doctrine check: every dir has index.md; index links every sibling file/dir.""" import re, sys from pathlib import Path kb = Path(sys.argv[1]) issues = [] dirs = sorted({p.parent for p in kb.rglob("*.md")}) for d in dirs: idx = d / "index.md" if not idx.is_file() and d != kb: issues.append(f"{d.relative_to(kb)}: no index.md") continue if not idx.is_file(): continue text = idx.read_text(errors="replace") linked = set() for m in re.finditer(r"\]\((?!https?:|#)([^)#\s]+?)(?:#[^)]*)?\)", text): t = m.group(1) target = (d / t).resolve() if not t.startswith("/") else (kb / t.lstrip("/")).resolve() linked.add(target) # wikilinks too wl_names = {m.group(1).strip().rsplit("/", 1)[-1] for m in re.finditer(r"\[\[([^\]|#]+)", text)} for sib in sorted(d.iterdir()): if sib.suffix != ".md" or sib.name == "index.md": continue if sib.resolve() not in linked and sib.stem not in wl_names: issues.append(f"{d.relative_to(kb)}/index.md: does not link {sib.name}") # subdirs of d linked as their index? for sub in sorted(d.iterdir()): if sub.is_dir() and (sub / "index.md").is_file(): subidx = (sub / "index.md").resolve() dirref = sub.resolve() if subidx not in linked and dirref not in linked: # also accept link to sub/ or sub/index.md or wikilink to dir name if not re.search(re.escape(sub.name) + r"[/)]", text): issues.append(f"{d.relative_to(kb)}/index.md: does not link subdir {sub.name}/") for i in issues: print(i) print(f"index-coverage: {len(issues)} issue(s)") sys.exit(1 if issues else 0)