Files
zed_time_tracker/README.md
T

240 lines
7.2 KiB
Markdown

# Zed Hours Tracker
A lightweight, local-first, background time tracker for Zed editor sessions, with automatic git commit logging. No cloud, no accounts, no manual timers.
Works on **Arch Linux + Wayland** and is designed to be portable to other projects.
---
## What it does
1. **Detects when you are working in Zed** on this project by monitoring the active window title (Wayland) and filesystem activity.
2. **Writes heartbeats** to `.zed-hours/heartbeats.jsonl`:
- timestamp
- project path
- focused file (if detectable)
- idle status
3. **Logs git commits** automatically via `post-commit` hook to `.zed-hours/commits.jsonl`.
4. **Generates markdown reports** combining heartbeats + commits into daily summaries.
---
## Files
| File | Purpose |
|---|---|
| `install.py` | Install git hook and create `.zed-hours/` data directory |
| `log_commit.py` | Called by git hook; do not run directly usually |
| `zed_tracker.py` | Background tracker for Zed activity |
| `generate_report.py` | Generate a markdown hours report |
| `sync.py` | Opt-in incremental sync of local logs to a Zed Hours server |
| `README.md` | This file |
---
## Installation
Run from the project root:
```bash
python3 tools/time_tracking/install.py
```
This will:
- create `.zed-hours/` in the project root;
- install a `post-commit` git hook that logs commits;
- create a default config file.
---
## Running the tracker
The tracker is intentionally a simple script you run in the background.
### Option A: One-off test
```bash
python3 tools/time_tracking/zed_tracker.py --project-root $(pwd)
```
Press `Ctrl+C` to stop.
### Option B: Zed tasks (run from the editor)
This project includes Zed tasks so you can trigger tracker actions with the task runner shortcut (`alt+shift+t` or `ctrl+shift+t` depending on your keymap). The default working directory for Zed tasks is the project root, so the commands use relative paths.
| Task | Action |
|---|---|
| `Zed Hours: Install tracker` | Install git hook, config, and systemd service |
| `Zed Hours: Start tracker (foreground)` | Start the tracker in a Zed terminal panel |
| `Zed Hours: Generate report (today)` | Generate today's report |
| `Zed Hours: Generate report (yesterday)` | Generate yesterday's report |
| `Zed Hours: Generate report (last 14 days)` | Generate a 14-day report |
| `Zed Hours: Open latest report` | Print path to latest report |
| `Zed Hours: Status` | Show heartbeat/commit/report counts |
### Option C: Background via systemd user service (recommended for daily use)
A sample service file is generated by `install.py` at:
```
~/.config/systemd/user/zed-hours-gem360.service
```
Enable it:
```bash
systemctl --user daemon-reload
systemctl --user enable --now zed-hours-gem360.service
systemctl --user status zed-hours-gem360.service
```
Logs:
```bash
journalctl --user -u zed-hours-gem360.service -f
```
To stop:
```bash
systemctl --user stop zed-hours-gem360.service
```
### Option C: `tmux` / `screen`
```bash
tmux new -s zed-hours -d 'python3 tools/time_tracking/zed_tracker.py --project-root $(pwd)'
```
---
## Automatic sync (systemd user timer)
`sync_all.py` syncs every configured project in one run. Configuration lives
in `~/.config/zed-hours-sync/`:
| File | Purpose |
|---|---|
| `env` | `ZED_HOURS_SERVER_URL`, `ZED_HOURS_SERVER_TOKEN`, `ZED_HOURS_DEVICE_ID` (keep mode 600) |
| `projects.txt` | One project per line: `PATH [SLUG]`; slug defaults to the directory name |
A systemd user pair drives it:
- `zed-hours-sync.service` (oneshot; runs `sync_all.py` with the env file)
- `zed-hours-sync.timer` (every 15 minutes, `Persistent=true` to catch up after sleep)
Useful commands:
```sh
systemctl --user start zed-hours-sync.service # run a sync now
systemctl --user list-timers zed-hours-sync.timer # next scheduled run
journalctl --user -u zed-hours-sync.service -f # live logs
```
---
## Generating a report
```bash
python3 tools/time_tracking/generate_report.py --from 2026-08-03 --to yesterday
```
Output is written to `.zed-hours/reports/YYYY-MM-DD_to_YYYY-MM-DD.md`.
You can also run:
```bash
python3 tools/time_tracking/generate_report.py --today
```
---
## Optional server sync
The tracker remains local-first. To opt in to sending the locally recorded heartbeats and commits to a compatible server, set these environment variables:
```bash
export ZED_HOURS_SERVER_URL="https://hours.example.com"
export ZED_HOURS_SERVER_TOKEN="your-bearer-token"
export ZED_HOURS_DEVICE_ID="laptop-1"
```
Then run from the tracked project root:
```bash
python3 tools/time_tracking/sync.py
```
The command posts heartbeats to `/api/v1/sync/zed-heartbeats` and commits to `/api/v1/sync/zed-commits`. It sends `device_id`, `project_slug`, and records in batches. The slug defaults to the project root directory name and can be overridden without exposing the absolute local project path:
```bash
python3 tools/time_tracking/sync.py \
--project-root /path/to/project \
--project-slug customer-portal
```
Progress is recorded locally in `.zed-hours/sync-state.json`. Retrying after a failed request is safe: each record has a deterministic, device-specific `source_id`, allowing the server to treat duplicate submissions as idempotent.
---
## How activity detection works on Wayland
Wayland does not expose a global active-window API for security reasons. The tracker tries several methods, in order:
1. **Hyprland**: `hyprctl activewindow`
2. **Sway**: `swaymsg -t get_tree`
3. **GNOME / Mutter**: D-Bus `GetActiveWindow` via `gdbus`
4. **KDE / KWin**: D-Bus via `qdbus`
5. **Fallback**: monitor filesystem activity inside the project root using `inotify` or polling recent file access/modification times.
If all window-detection methods fail, the fallback still gives you a reliable signal that someone is actively editing files in this project.
---
## Reusing in another project
Copy the `tools/time_tracking/` directory to the new project and run:
```bash
python3 tools/time_tracking/install.py
```
The scripts detect the project root automatically from the current working directory.
---
## Data format
### `.zed-hours/heartbeats.jsonl`
```json
{"timestamp": "2026-08-26T09:15:00", "project": "/home/fegger/Code/ixsol/gem360-git", "file": "README.md", "active": true, "source": "hyprctl"}
```
### `.zed-hours/commits.jsonl`
```json
{"type": "commit", "timestamp": "2026-08-26T09:20:00", "repo": "/home/fegger/Code/ixsol/gem360-git", "branch": "va_module", "sha": "abc12345", "message": "[ADD] feature X", "author": "Florian Egger"}
```
### `.zed-hours/config.json`
```json
{
"project_root": "/home/fegger/Code/ixsol/gem360-git",
"heartbeat_interval_seconds": 60,
"idle_threshold_seconds": 300,
"log_dir": ".zed-hours"
}
```
---
## Notes
- This tool is intentionally simple and local. It is not a replacement for a commercial time tracker, but it is a reliable fallback when you forget to log hours.
- The tracker does not send data anywhere unless you explicitly run `sync.py` with `ZED_HOURS_SERVER_URL`, `ZED_HOURS_SERVER_TOKEN`, and `ZED_HOURS_DEVICE_ID` configured.
- Heartbeat granularity is one minute by default. Tune it in `config.json`.
- Because of Wayland security, window detection may not give the exact filename. The filesystem fallback compensates for this.