fd50b7deb9
Provide a portable, cache-ignored Obsidian vault lookup plugin and bind it to Super+N.
76 lines
2.2 KiB
Python
76 lines
2.2 KiB
Python
#!/usr/bin/env python3
|
|
"""Safely create one direct child in the configured knowledge vault."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
|
|
def fail(message: str) -> int:
|
|
print(message, file=sys.stderr)
|
|
return 1
|
|
|
|
|
|
def child_name(raw: str) -> str | None:
|
|
name = raw.strip()
|
|
if not name or name in {".", ".."} or "/" in name or "\\" in name or "\0" in name:
|
|
return None
|
|
if name.startswith("."):
|
|
return None
|
|
return name
|
|
|
|
|
|
def main() -> int:
|
|
if len(sys.argv) != 5:
|
|
return fail("usage: create-entry.py VAULT KIND CURRENT_DIRECTORY NAME")
|
|
raw_vault, kind, current_directory, raw_name = sys.argv[1:]
|
|
vault = Path(raw_vault).expanduser().resolve()
|
|
if not vault.is_dir():
|
|
return fail(f"vault does not exist: {vault}")
|
|
if kind not in {"note", "directory"}:
|
|
return fail("kind must be note or directory")
|
|
current = (vault / current_directory).resolve()
|
|
if current != vault and vault not in current.parents:
|
|
return fail("current directory escaped the vault")
|
|
if not current.is_dir():
|
|
return fail("current directory no longer exists")
|
|
|
|
name = child_name(raw_name)
|
|
if name is None:
|
|
return fail("use a non-hidden direct name without slashes")
|
|
if kind == "note" and not name.lower().endswith(".md"):
|
|
name += ".md"
|
|
target = (current / name).resolve()
|
|
if target.parent != current:
|
|
return fail("target escaped the current directory")
|
|
|
|
try:
|
|
if kind == "directory":
|
|
target.mkdir()
|
|
else:
|
|
title = target.stem
|
|
with target.open("x", encoding="utf-8") as handle:
|
|
handle.write(f"# {title}\n")
|
|
except FileExistsError:
|
|
return fail(f"already exists: {name}")
|
|
except OSError as error:
|
|
return fail(f"cannot create {name}: {error}")
|
|
|
|
relative = target.relative_to(vault).as_posix()
|
|
json.dump(
|
|
{
|
|
"kind": kind,
|
|
"relative": relative,
|
|
"name": name,
|
|
"absolute": str(target),
|
|
},
|
|
sys.stdout,
|
|
)
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|