Erste Fassung: Claude-Werkzeuge aus dem Vault

.vault-tools (ohne trading-plattform-pruefung und watermarks-remover),
.claude/agents, .claude/skills, .claude/workflows, .claude/settings.json.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
Pascal MichelandClaude Opus 5.5 committed 2026-10-14 11:27:59 +02:00
commit fa1c26b407
137 files changed
+43926

No files matched your search

@@ -0,0 +1,98 @@
"""Liest YAML-Frontmatter und schreibt einzelne Felder zurück, ohne den Rest anzufassen."""
from __future__ import annotations
import re
from dataclasses import dataclass
from pathlib import Path
TRENNER = "---"
SKALARE_ZEILE = re.compile(r"^([A-Za-z_][A-Za-z0-9_-]*):\s*(.*)$")
@dataclass(frozen=True)
class Notiz:
pfad: Path
frontmatter: dict[str, str]
rumpf: str
roh: str
def _zerlege(roh: str) -> tuple[list[str], str]:
"""Liefert (Frontmatter-Zeilen ohne Trenner, Rumpf). Ohne Frontmatter: ([], roh)."""
if not roh.startswith(TRENNER + "\n"):
return [], roh
rest = roh[len(TRENNER) + 1 :]
ende = rest.find("\n" + TRENNER + "\n")
if ende == -1:
return [], roh
kopf = rest[:ende].split("\n")
rumpf = rest[ende + len(TRENNER) + 2 :]
return kopf, rumpf.lstrip("\n")
def _entpacke(wert: str) -> str:
wert = wert.strip()
if len(wert) >= 2 and wert[0] == wert[-1] and wert[0] in "\"'":
return wert[1:-1]
return wert
def _verpacke(wert: str) -> str:
"""Quotet nur, wo YAML es wirklich braucht.
Ein Doppelpunkt allein reicht nicht: „sha256:abc" und „2026-08-05T12:00:00+02:00"
sind gültige einfache Skalare. Erst „Doppelpunkt plus Leerzeichen" beendet einen
Schlüssel. Würde hier jeder Doppelpunkt quoten, stünde in jeder Notiz
bookstack_hash: "sha256:…" statt bookstack_hash: sha256:…
"""
if not wert:
return '""'
unsicher = (
": " in wert
or wert.endswith(":")
or " #" in wert
or "\n" in wert
or wert != wert.strip()
or wert[0] in "\"'[]{}>|*&!%@`#"
)
if unsicher:
return '"' + wert.replace('"', '\\"') + '"'
return wert
def lade_notiz(pfad: Path) -> Notiz:
roh = pfad.read_text(encoding="utf-8")
kopf, rumpf = _zerlege(roh)
felder: dict[str, str] = {}
for zeile in kopf:
treffer = SKALARE_ZEILE.match(zeile)
if treffer and not treffer.group(2).startswith("["):
felder[treffer.group(1)] = _entpacke(treffer.group(2))
return Notiz(pfad=pfad, frontmatter=felder, rumpf=rumpf, roh=roh)
def setze_felder(roh: str, werte: dict[str, str]) -> str:
"""Setzt oder ersetzt Frontmatter-Felder und lässt alles andere zeichengleich."""
kopf, rumpf = _zerlege(roh)
if not kopf and not roh.startswith(TRENNER + "\n"):
neue = [f"{name}: {_verpacke(wert)}" for name, wert in werte.items()]
return TRENNER + "\n" + "\n".join(neue) + "\n" + TRENNER + "\n" + roh
offen = dict(werte)
ergebnis: list[str] = []
for zeile in kopf:
treffer = SKALARE_ZEILE.match(zeile)
name = treffer.group(1) if treffer else None
if name in offen:
ergebnis.append(f"{name}: {_verpacke(offen.pop(name))}")
else:
ergebnis.append(zeile)
ergebnis.extend(f"{name}: {_verpacke(wert)}" for name, wert in offen.items())
return TRENNER + "\n" + "\n".join(ergebnis) + "\n" + TRENNER + "\n\n" + rumpf
def schreibe_felder(pfad: Path, werte: dict[str, str]) -> None:
roh = pfad.read_text(encoding="utf-8")
pfad.write_text(setze_felder(roh, werte), encoding="utf-8", newline="\n")