#!/usr/bin/env python3 """Install the Zed Hours Tracker git hook and config for the current project.""" import json import os import stat import subprocess from pathlib import Path def get_project_root() -> Path: """Return the git project root, falling back to the script's project directory.""" try: root = subprocess.check_output( ["git", "rev-parse", "--show-toplevel"], text=True, stderr=subprocess.DEVNULL, ).strip() return Path(root).resolve() except (subprocess.CalledProcessError, FileNotFoundError): pass # Fallback 1: directory containing this script's grandparent (/tools/time_tracking/install.py) script_project = Path(__file__).resolve().parents[2] if (script_project / ".git").exists() or (script_project / ".zed-hours").exists(): return script_project # Fallback 2: current working directory return Path.cwd().resolve() def ensure_log_dir(project_root: Path) -> Path: log_dir = project_root / ".zed-hours" log_dir.mkdir(parents=True, exist_ok=True) return log_dir def write_config(log_dir: Path, project_root: Path) -> Path: config = { "project_root": str(project_root), "log_dir": str(log_dir.relative_to(project_root)), "heartbeat_interval_seconds": 60, "idle_threshold_seconds": 300, "max_inactive_gap_seconds": 600, } config_path = log_dir / "config.json" if not config_path.exists(): config_path.write_text(json.dumps(config, indent=2) + "\n", encoding="utf-8") print(f"Created config: {config_path}") else: print(f"Config already exists: {config_path}") return config_path def install_git_hook(project_root: Path) -> None: hook_path = project_root / ".git" / "hooks" / "post-commit" if not hook_path.parent.exists(): print("No .git directory found; skipping git hook install.") return script_dir = project_root / "tools" / "time_tracking" log_commit_script = script_dir / "log_commit.py" hook_content = f"""#!/bin/sh # Zed Hours Tracker - post-commit hook python3 "{log_commit_script}" "$@" """ if hook_path.exists(): existing = hook_path.read_text(encoding="utf-8") if "Zed Hours Tracker" in existing: print(f"Hook already installed: {hook_path}") return # Append to existing hook hook_path.write_text(existing.rstrip("\n") + "\n\n" + hook_content, encoding="utf-8") else: hook_path.write_text(hook_content, encoding="utf-8") # Make executable hook_path.chmod(hook_path.stat().st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH) print(f"Installed git hook: {hook_path}") def write_systemd_service(project_root: Path, script_dir: Path) -> None: """Write a sample systemd user service file for background tracking.""" systemd_dir = Path.home() / ".config" / "systemd" / "user" service_name = f"zed-hours-{project_root.name}.service" service_path = systemd_dir / service_name tracker_script = script_dir / "zed_tracker.py" service_content = f"""[Unit] Description=Zed Hours Tracker for {project_root.name} After=graphical-session.target [Service] Type=simple WorkingDirectory={project_root} ExecStart=/usr/bin/python3 {tracker_script} --project-root {project_root} Restart=on-failure RestartSec=10 Environment="PYTHONUNBUFFERED=1" [Install] WantedBy=default.target """ systemd_dir.mkdir(parents=True, exist_ok=True) service_path.write_text(service_content, encoding="utf-8") print(f"Wrote systemd user service: {service_path}") print(f"Enable with: systemctl --user daemon-reload && systemctl --user enable --now {service_name}") def main() -> None: project_root = get_project_root() script_dir = Path(__file__).resolve().parent log_dir = ensure_log_dir(project_root) write_config(log_dir, project_root) install_git_hook(project_root) write_systemd_service(project_root, script_dir) print(f"\nZed Hours Tracker installed for project: {project_root}") print(f"Data directory: {log_dir}") print("\nNext steps:") print(" 1. Start the tracker: python3 tools/time_tracking/zed_tracker.py") print(" 2. Or enable the systemd service shown above.") print(" 3. Make a commit to test the git hook.") print(" 4. Generate a report: python3 tools/time_tracking/generate_report.py --today") if __name__ == "__main__": main()