#!/usr/bin/env python3 """Daily Coding Summary for Obsidian Track coding activity from Zed editors and Git commits, then write a summary into your Obsidian daily note. Usage: python main.py python main.py --vault ~/Obsidian/MyVault python main.py --code-dir ~/Code --code-dir ~/Work python main.py --date 2026-05-20 """ import argparse import sys from datetime import datetime, timezone from pathlib import Path from utils.config import date_range, get_vault_path from utils.git_tracker import fetch_git_activity from utils.obsidian_writer import write_daily_summary from utils.zed_tracker import fetch_zed_activity def parse_date(date_str: str) -> datetime: """Parse YYYY-MM-DD string into a datetime object.""" try: dt = datetime.strptime(date_str, "%Y-%m-%d") return dt.replace(tzinfo=timezone.utc).astimezone() except ValueError: raise argparse.ArgumentTypeError( f"Invalid date format: '{date_str}'. Expected YYYY-MM-DD." ) def main(): parser = argparse.ArgumentParser( description="Track daily coding activity and write it to Obsidian." ) parser.add_argument( "--vault", dest="vault", default=None, help="Path to the Obsidian vault. Overrides OBSIDIAN_VAULT env var.", ) parser.add_argument( "--code-dir", dest="code_dirs", action="append", default=None, help="Directory to scan for git repos (default: ~/Code). Can be given multiple times.", ) parser.add_argument( "--daily-folder", dest="daily_folder", default=None, help="Folder inside the vault for daily notes (default: Daily Notes). Overrides OBSIDIAN_DAILY_FOLDER env var.", ) parser.add_argument( "--date", dest="date", type=parse_date, default=None, help="Date to summarize (YYYY-MM-DD). Defaults to today.", ) parser.add_argument( "--no-zed", dest="no_zed", action="store_true", help="Skip Zed editor tracking.", ) parser.add_argument( "--no-git", dest="no_git", action="store_true", help="Skip Git commit tracking.", ) parser.add_argument( "--dry-run", dest="dry_run", action="store_true", help="Print the summary instead of writing to Obsidian.", ) args = parser.parse_args() try: vault_path = get_vault_path(args.vault) except Exception as e: print(f"Error: {e}", file=sys.stderr) sys.exit(1) if args.daily_folder: daily_folder = args.daily_folder else: import os daily_folder = os.environ.get("OBSIDIAN_DAILY_FOLDER", "Daily Notes") code_dirs = args.code_dirs if code_dirs: code_dirs = [Path(d).expanduser().resolve() for d in code_dirs] else: code_dirs = [Path.home() / "Code"] target_date = args.date or datetime.now(timezone.utc).astimezone() start_ts, end_ts = date_range(target_date) zed_projects = [] git_repos = [] if not args.no_zed: print("🔍 Reading Zed editor history…") zed_projects = fetch_zed_activity(start_ts, end_ts, code_dirs=code_dirs) print(f" Found {len(zed_projects)} project(s) with editor activity.") if not args.no_git: print("🔍 Scanning git repositories…") git_repos = fetch_git_activity(start_ts, end_ts, code_dirs=code_dirs) print(f" Found {len(git_repos)} repo(s) with commits today.") if args.dry_run: from utils.obsidian_writer import generate_markdown print("\n--- Daily Summary ---\n") print(generate_markdown(zed_projects, git_repos, target_date)) print("\n---------------------") return note_path = write_daily_summary( vault_path=vault_path, zed_projects=zed_projects, git_repos=git_repos, daily_folder=daily_folder, date=target_date, ) print(f"✅ Daily note updated: {note_path}") if __name__ == "__main__": main()