Files
2025-11-30 08:35:59 +08:00

148 lines
4.1 KiB
Python

#!/usr/bin/env python3
# /// script
# dependencies = ["python-frontmatter", "pyyaml"]
# ///
"""
Create a new note in the Obsidian vault with proper frontmatter.
Usage:
uv run create-note.py "title" "content" [--folder path] [--tags tag1,tag2] [--related "[[Note]]"]
Arguments:
title - Note title (will be used for filename)
content - Note content (markdown)
--folder - Folder path relative to vault (default: Geoffrey/Research)
--tags - Comma-separated tags
--related - Comma-separated related notes as wiki-links
Examples:
uv run create-note.py "AI Model Comparison" "Content..." --folder Geoffrey/Research --tags research,ai
uv run create-note.py "Meeting Notes" "Content..." --folder Meetings --related "[[John Smith]],[[Project]]"
"""
import sys
import os
import json
import re
from pathlib import Path
from datetime import datetime
import frontmatter
VAULT_PATH = Path.home() / "Library/Mobile Documents/iCloud~md~obsidian/Documents/Personal_Notes"
def slugify(title: str) -> str:
"""Convert title to filename-safe slug."""
# Replace spaces with hyphens, remove special chars
slug = re.sub(r'[^\w\s-]', '', title.lower())
slug = re.sub(r'[-\s]+', '-', slug).strip('-')
return slug
def create_note(
title: str,
content: str,
folder: str = "Geoffrey/Research",
tags: list[str] = None,
related: list[str] = None
) -> str:
"""Create a new note with frontmatter."""
# Ensure folder exists
folder_path = VAULT_PATH / folder
folder_path.mkdir(parents=True, exist_ok=True)
# Generate filename
date_prefix = datetime.now().strftime("%Y-%m-%d")
slug = slugify(title)
filename = f"{date_prefix}-{slug}.md"
file_path = folder_path / filename
# Check if file exists
if file_path.exists():
# Add timestamp to make unique
timestamp = datetime.now().strftime("%H%M%S")
filename = f"{date_prefix}-{slug}-{timestamp}.md"
file_path = folder_path / filename
# Build frontmatter
metadata = {
"created": datetime.now().strftime("%Y-%m-%d"),
"source": "geoffrey"
}
if tags:
metadata["tags"] = ["geoffrey"] + [t.strip() for t in tags if t.strip()]
else:
metadata["tags"] = ["geoffrey"]
if related:
metadata["related"] = [r.strip() for r in related if r.strip()]
# Create the post
post = frontmatter.Post(content)
post.metadata = metadata
# Add title as H1 if not already present
if not content.strip().startswith('#'):
post.content = f"# {title}\n\n{content}"
# Write file
with open(file_path, 'w', encoding='utf-8') as f:
f.write(frontmatter.dumps(post))
return str(file_path.relative_to(VAULT_PATH))
def main():
if len(sys.argv) < 3:
print("Usage: uv run create-note.py \"title\" \"content\" [--folder path] [--tags t1,t2] [--related \"[[Note]]\"]")
sys.exit(1)
title = sys.argv[1]
content = sys.argv[2]
folder = "Geoffrey/Research"
tags = []
related = []
# Parse arguments
i = 3
while i < len(sys.argv):
if sys.argv[i] == "--folder" and i + 1 < len(sys.argv):
folder = sys.argv[i + 1]
i += 2
elif sys.argv[i] == "--tags" and i + 1 < len(sys.argv):
tags = sys.argv[i + 1].split(',')
i += 2
elif sys.argv[i] == "--related" and i + 1 < len(sys.argv):
related = sys.argv[i + 1].split(',')
i += 2
else:
i += 1
print(f"Creating note: {title}")
print(f"Folder: {folder}")
if tags:
print(f"Tags: {', '.join(tags)}")
if related:
print(f"Related: {', '.join(related)}")
relative_path = create_note(title, content, folder, tags, related)
full_path = VAULT_PATH / relative_path
print(f"\nNote created: {relative_path}")
# Output JSON for programmatic use
result = {
"success": True,
"path": relative_path,
"full_path": str(full_path),
"folder": folder,
"title": title
}
print(f"\n{json.dumps(result)}")
if __name__ == "__main__":
main()