vault-cli: extract to standalone repo, add flake.nix, move one-off scripts to scripts/one-off/
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Add a short-name alias (ID prefix stripped) to every ID-prefixed note's
|
||||
frontmatter, so Obsidian's alias-autocomplete can produce clean [[ID|Name]]
|
||||
links without the user typing the ID. Idempotent: skips notes that already
|
||||
have that alias or non-ID-prefixed notes.
|
||||
"""
|
||||
import os, re, sys
|
||||
|
||||
VAULT = "/vault"
|
||||
# Matches "F.50.12 Jen", "R.31.10 Hindi", "F.22.11 Places We've Slept" etc.
|
||||
ID_TITLE_RE = re.compile(r'^([RJFI](?:\.\d+){1,3})\s+(.+)$')
|
||||
|
||||
FRONTMATTER_RE = re.compile(r'^---\n(.*?)\n---\n', re.DOTALL)
|
||||
|
||||
def split_frontmatter(text):
|
||||
m = FRONTMATTER_RE.match(text)
|
||||
if m:
|
||||
return m.group(1), text[m.end():]
|
||||
return None, text
|
||||
|
||||
def add_alias(fm_text, alias):
|
||||
lines = fm_text.split("\n") if fm_text else []
|
||||
# find aliases: line (list style) or aliases: [a, b]
|
||||
alias_line_idx = None
|
||||
for i, l in enumerate(lines):
|
||||
if re.match(r'^aliases\s*:', l):
|
||||
alias_line_idx = i
|
||||
break
|
||||
if alias_line_idx is None:
|
||||
lines.append(f"aliases:\n - {alias}")
|
||||
return "\n".join(lines)
|
||||
line = lines[alias_line_idx]
|
||||
# inline list form: aliases: [a, b]
|
||||
m = re.match(r'^aliases\s*:\s*\[(.*)\]\s*$', line)
|
||||
if m:
|
||||
existing = [x.strip().strip('"\'') for x in m.group(1).split(",") if x.strip()]
|
||||
if alias in existing:
|
||||
return None # already present
|
||||
existing.append(alias)
|
||||
lines[alias_line_idx] = "aliases: [" + ", ".join(existing) + "]"
|
||||
return "\n".join(lines)
|
||||
# block list form: aliases:\n - a\n - b
|
||||
if line.strip() == "aliases:" or re.match(r'^aliases\s*:\s*$', line):
|
||||
j = alias_line_idx + 1
|
||||
existing = []
|
||||
while j < len(lines) and re.match(r'^\s*-\s*', lines[j]):
|
||||
existing.append(lines[j].strip()[2:].strip().strip('"\''))
|
||||
j += 1
|
||||
if alias in existing:
|
||||
return None
|
||||
lines.insert(j, f" - {alias}")
|
||||
return "\n".join(lines)
|
||||
# single scalar alias: aliases: Foo
|
||||
scalar = line.split(":", 1)[1].strip().strip('"\'')
|
||||
if scalar == alias:
|
||||
return None
|
||||
lines[alias_line_idx] = "aliases:\n - " + scalar + f"\n - {alias}"
|
||||
return "\n".join(lines)
|
||||
|
||||
def main():
|
||||
execute = "--execute" in sys.argv
|
||||
changed = 0
|
||||
skipped_no_id = 0
|
||||
skipped_has_alias = 0
|
||||
errors = []
|
||||
for dp, _, fns in os.walk(VAULT):
|
||||
if ".git" in dp.split(os.sep):
|
||||
continue
|
||||
for fn in fns:
|
||||
if not fn.lower().endswith(".md"):
|
||||
continue
|
||||
base = fn[:-3]
|
||||
m = ID_TITLE_RE.match(base)
|
||||
if not m:
|
||||
skipped_no_id += 1
|
||||
continue
|
||||
alias = m.group(2)
|
||||
p = os.path.join(dp, fn)
|
||||
try:
|
||||
with open(p, "r", encoding="utf-8", errors="ignore") as f:
|
||||
text = f.read()
|
||||
except Exception as e:
|
||||
errors.append((p, str(e)))
|
||||
continue
|
||||
fm, rest = split_frontmatter(text)
|
||||
new_fm = add_alias(fm or "", alias)
|
||||
if new_fm is None:
|
||||
skipped_has_alias += 1
|
||||
continue
|
||||
new_text = f"---\n{new_fm}\n---\n\n{rest.lstrip(chr(10))}" if fm is None else f"---\n{new_fm}\n---\n{rest}"
|
||||
changed += 1
|
||||
if execute:
|
||||
with open(p, "w", encoding="utf-8") as f:
|
||||
f.write(new_text)
|
||||
|
||||
print(f"Notes with ID-prefixed title: {changed + skipped_has_alias}")
|
||||
print(f" Would add alias: {changed}")
|
||||
print(f" Already had alias: {skipped_has_alias}")
|
||||
print(f"Notes without ID prefix (skipped, not applicable): {skipped_no_id}")
|
||||
if errors:
|
||||
print(f"Errors: {len(errors)}")
|
||||
for p, e in errors[:10]:
|
||||
print(f" {p}: {e}")
|
||||
if not execute:
|
||||
print("\n(dry run -- pass --execute to write changes)")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user