#!/usr/bin/env python3 """Legt eine neue Notiz aus einem Template an — mit korrektem Frontmatter, korrektem Ordner und stabiler uid. Von Hand geschriebenes Frontmatter driftet; deshalb dieser Weg. Bestehende Dateien werden nie überschrieben. Beispiele: python3 scripts/new_note.py --type project-code --title "Auth Service" \\ --tags go,oidc --summary "OIDC-Auth für die interne Toolchain" python3 scripts/new_note.py --type research --title "Rate Limiting Strategien" \\ --project auth-service python3 scripts/new_note.py --type decision --title "Postgres statt Mongo" \\ --project auth-service """ from __future__ import annotations import argparse import os import sys sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) import vaultlib as V # noqa: E402 SKILL_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) TEMPLATE_DIR = os.path.join(SKILL_DIR, "assets", "templates") # type -> (Zielpfad-Muster, eigener Ordner?) TARGETS = { "project-code": ("10_Projects/{slug}/index.md", True), "template-project": ("40_Templates/project-templates/{slug}/index.md", True), "research": ("30_Research/{slug}.md", False), "area": ("20_Areas/{slug}.md", False), "resource": ("50_Resources/{slug}.md", False), "moc": ("20_Areas/{slug}.md", False), "decision": ("10_Projects/{project}/decisions/{slug}.md", False), "inbox": ("00_Inbox/{slug}.md", False), } DEFAULT_STATUS = { "project-code": "active", "research": "active", "area": "reference", "resource": "reference", "moc": "reference", "decision": "done", "template-project": "reference", "inbox": "idea", } def main() -> int: ap = argparse.ArgumentParser(description="Notiz aus Template anlegen") ap.add_argument("--vault", default=".") ap.add_argument("--type", required=True, choices=sorted(TARGETS)) ap.add_argument("--title", required=True) ap.add_argument("--summary", default="") ap.add_argument("--tags", default="") ap.add_argument("--status", default="") ap.add_argument("--project", default="", help="Projekt-Slug (Pflicht bei --type decision)") ap.add_argument("--out", default="", help="Zielpfad relativ zum Vault überschreiben") ap.add_argument("--print-only", action="store_true", help="Inhalt ausgeben statt schreiben") args = ap.parse_args() vault = os.path.abspath(os.path.expanduser(args.vault)) slug = V.slugify(args.title) pattern, _ = TARGETS[args.type] if args.out: rel = args.out else: if "{project}" in pattern and not args.project: print("--project ist bei diesem Typ erforderlich", file=sys.stderr) return 2 rel = pattern.format(slug=slug, project=V.slugify(args.project or "misc")) dest = os.path.join(vault, rel) if os.path.exists(dest) and not args.print_only: print(f"Existiert bereits: {rel} — nichts geschrieben.", file=sys.stderr) return 1 tpl_path = os.path.join(TEMPLATE_DIR, f"{args.type}.md") if not os.path.exists(tpl_path): tpl_path = os.path.join(TEMPLATE_DIR, "resource.md") with open(tpl_path, "r", encoding="utf-8") as fh: tpl = fh.read() today = V.today() values = { "{{uid}}": f"{today.replace('-', '')}-{slug[:40]}", "{{title}}": args.title, "{{summary}}": args.summary or "TODO: ein Satz, worum es geht.", "{{status}}": args.status or DEFAULT_STATUS.get(args.type, "active"), "{{tags}}": ", ".join(V.as_list(args.tags)), "{{project}}": args.project, "{{date}}": today, "{{slug}}": slug, } content = tpl for k, v in values.items(): content = content.replace(k, v) if args.print_only: print(content) return 0 os.makedirs(os.path.dirname(dest), exist_ok=True) with open(dest, "w", encoding="utf-8") as fh: fh.write(content) print(rel) print("Angelegt. Danach: summary prüfen, Referenzen ergänzen, " "build_index.py laufen lassen.") return 0 if __name__ == "__main__": raise SystemExit(main())