"""CLI entry point for org-to-quartz converter.""" import argparse import sys from pathlib import Path from .org_parser import parse_org_file from .markdown_writer import convert_document, write_markdown from .image_handler import copy_images, update_image_paths from .citations import CitationResolver def find_org_files(input_dir: Path) -> list[Path]: """Find all .org files in directory (recursive).""" return list(input_dir.rglob("*.org")) def convert_file( org_path: Path, input_dir: Path, output_dir: Path, citation_resolver: CitationResolver | None, verbose: bool = False, ) -> Path | None: """Convert a single org file to Quartz markdown. Args: org_path: Path to the org file input_dir: Root input directory (for computing relative paths) output_dir: Root output directory citation_resolver: Optional citation resolver verbose: Enable verbose output Returns: Path to created note directory, or None on error """ try: # Parse org file doc = parse_org_file(org_path) if verbose: print(f" Parsed: {doc.title or org_path.stem}") # Compute relative path from input dir to preserve directory structure relative_path = org_path.relative_to(input_dir) # Special case: root index.org becomes content/index.md directly if relative_path.stem == "index" and relative_path.parent == Path("."): note_dir = output_dir output_path = output_dir / "index.md" else: # Replace .org with directory containing index.md # e.g., concepts/zettelkasten.org -> concepts/zettelkasten/index.md note_relative_dir = relative_path.parent / relative_path.stem note_dir = output_dir / note_relative_dir output_path = note_dir / "index.md" # Create output directory for this note note_dir.mkdir(parents=True, exist_ok=True) # Copy images first (before conversion, to get path mapping) image_mapping = copy_images(doc, output_dir) if verbose and image_mapping: print(f" Copied {len(image_mapping)} images") # Convert document md_content = convert_document(doc, citation_resolver) # Update image paths in converted content md_content = update_image_paths(md_content, image_mapping) # Write output output_path.write_text(md_content, encoding="utf-8") return note_dir except Exception as e: print(f" Error converting {org_path.name}: {e}", file=sys.stderr) return None def main() -> int: """Main CLI entry point.""" parser = argparse.ArgumentParser( prog="org-to-quartz", description="Convert org-mode notes to Quartz-compatible markdown", ) parser.add_argument( "input_dir", type=Path, help="Directory containing .org files", ) parser.add_argument( "output_dir", type=Path, help="Output directory (e.g., quartz/content)", ) parser.add_argument( "--bib", type=Path, metavar="FILE", help="Path to .bib file for citation resolution", ) parser.add_argument( "-v", "--verbose", action="store_true", help="Verbose output", ) args = parser.parse_args() # Validate input directory if not args.input_dir.is_dir(): print(f"Error: Input directory does not exist: {args.input_dir}", file=sys.stderr) return 1 # Create output directory args.output_dir.mkdir(parents=True, exist_ok=True) # Find org files org_files = find_org_files(args.input_dir) if not org_files: print(f"No .org files found in {args.input_dir}", file=sys.stderr) return 1 print(f"Found {len(org_files)} org files") # Initialize citation resolver citation_resolver = CitationResolver(args.bib) if args.verbose: if citation_resolver.zotero.is_available(): print("Zotero Better BibTeX: available") else: print("Zotero Better BibTeX: not available") if args.bib: print(f"BibTeX file: {args.bib}") # Convert each file success_count = 0 error_count = 0 for org_path in org_files: print(f"Converting: {org_path.name}") result = convert_file(org_path, args.input_dir, args.output_dir, citation_resolver, args.verbose) if result: success_count += 1 if args.verbose: print(f" -> {result}") else: error_count += 1 # Summary print(f"\nDone: {success_count} converted, {error_count} errors") return 0 if error_count == 0 else 1 if __name__ == "__main__": sys.exit(main())