#!/usr/bin/env python3 """Add frontmatter to .md files that lack it. One-off agent-loop fix.""" import re import subprocess from pathlib import Path VAULT = Path(__file__).resolve().parent.parent FILES = """ decisions/2026-04-16-unifi-migration-peredelki.md decisions/2026-03-03-mailserver-setup-scenario.md decisions/2026-03-04-matrix-niikn-setup.md decisions/2026-04-14-niikn-openwrt-awg-fix.md decisions/2026-04-17-peredelki-podkop-stability-fix.md decisions/2026-02-26-clawdbot-129-cliproxy-fix.md decisions/2026-04-14-openclaw-claude-code-pipeline.md decisions/2026-04-17-code-server-upgrade.md notes/2026-02-26-knowledge-base-setup.md notes/2026-02-26-session-summary.md notes/2026-02-26-claude-code-session-clawdbot-fix.md notes/2026-02-26-full-session-log.md projects/unresolved-issues.md projects/nextcloud.md projects/infrastructure-overview.md projects/homelab-proxmox.md projects/video-surveillance.md projects/bitrix-sites.md projects/clawdbot-bots.md projects/all-projects-summary.md projects/dttb/npm-homelab.md projects/dttb/nextcloud.md projects/dttb/proxmox-inventory.md projects/dttb/server1c.md projects/dttb/agentdvr-home.md projects/dttb/memory-inventory.md projects/dttb/video-surveillance-report.md projects/dttb/openclaw.md projects/dttb/credentials.md projects/dttb/oleg-agent.md projects/dttb/netbird-inventory.md projects/dttb/clawdbot.md projects/dttb/clawdbot-znam.md projects/dttb/spaceweb-dns.md projects/dttb/npm-proxy-hosts.md projects/dttb/gpu-passthrough.md projects/dttb/openwrt-router.md projects/dttb/gitea.md projects/dttb/mailcow-dttb.md projects/dttb/videonablyudenie-znam.md projects/dttb/matrix-homelab.md projects/dttb/homeassistant.md projects/dttb/network-topology.md projects/glavtorg/instruction-diana-rdp.md projects/niikn/mailcow.md projects/niikn/NIIKN-Infrastructure.md projects/niikn/groupfolders-migration.md projects/niikn/changelog.md projects/niikn/matrix.md projects/niikn/clawdbot-niikn.md projects/niikn/proxmox.md projects/niikn/NIIKN-ChangeLog.md projects/niikn/npm.md projects/niikn/mikrotik.md projects/niikn/openwrt-bypass.md projects/niikn/NC-Talk-Setup.md projects/niikn/vpn.md projects/mmfb/proxmox-inventory.md snippets/proxmox-console-quirks.md snippets/clawdbot-cliproxy-config.md claude-memory/servicedesk-dttb.md claude-memory/benelux.md claude-memory/nextcloud-dttb.md claude-memory/nvr-fix.md claude-memory/videonablyudenie.md claude-memory/znamenskoe-home.md claude-memory/niikn-nextcloud.md claude-memory/krasnogorsk.md claude-memory/mas-niikn.md claude-memory/MEMORY.md """.strip().splitlines() TODAY = "2026-04-18" def file_date(rel_path: str, full: Path) -> str: name = full.name m = re.match(r"^(\d{4}-\d{2}-\d{2})", name) if m: return m.group(1) try: r = subprocess.run( ["git", "log", "--diff-filter=A", "--follow", "--format=%ad", "--date=short", "--", rel_path], cwd=VAULT, capture_output=True, text=True, timeout=10 ) lines = [l.strip() for l in r.stdout.splitlines() if l.strip()] if lines: return lines[-1] except Exception: pass return TODAY def type_for(rel_path: str) -> str: parts = rel_path.split("/") if parts[0] == "decisions": return "decision" if parts[0] == "notes": return "note" if parts[0] == "daily": return "daily" if parts[0] == "projects": return "project" if parts[0] == "snippets": return "reference" if parts[0] == "claude-memory": return "reference" if parts[0] == "templates": return "template" return "note" def tags_for(rel_path: str) -> list: parts = rel_path.split("/") tags = [] if parts[0] == "decisions": tags.append("decision") elif parts[0] == "notes": tags.append("note") elif parts[0] == "snippets": tags.append("snippet") elif parts[0] == "claude-memory": tags.append("memory") if len(parts) > 1 and parts[0] == "projects": proj = parts[1] if proj in ("dttb", "niikn", "glavtorg", "mmfb", "krasnogorsk"): tags.append(proj) lower = rel_path.lower() if "nextcloud" in lower: tags.append("nextcloud") if "matrix" in lower: tags.append("matrix") if "proxmox" in lower: tags.append("proxmox") if "npm" in lower: tags.append("npm") if "dns" in lower: tags.append("dns") if "mailcow" in lower or "mail" in lower.split("/")[-1]: tags.append("mail") if "clawdbot" in lower or "openclaw" in lower: tags.append("bot") if "vpn" in lower or "openwrt" in lower or "netbird" in lower: tags.append("network") if "video" in lower or "surveillance" in lower or "camera" in lower or "agentdvr" in lower: tags.append("video") if not tags: tags = ["note"] seen = set() out = [] for t in tags: if t not in seen: seen.add(t) out.append(t) return out def add_fm(rel_path: str): full = VAULT / rel_path if not full.is_file(): print(f"MISSING: {rel_path}") return text = full.read_text() if text.startswith("---\n"): print(f"HAS-FM: {rel_path}") return d = file_date(rel_path, full) t = type_for(rel_path) tags = tags_for(rel_path) fm = ( "---\n" f"date: {d}\n" f"type: {t}\n" f"tags: [{', '.join(tags)}]\n" "---\n\n" ) full.write_text(fm + text) print(f"FIXED: {rel_path} (date={d}, type={t}, tags={tags})") if __name__ == "__main__": for line in FILES: line = line.strip() if not line: continue add_fm(line)