#!/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())