Track all your activities on Mac – automatically.
Analyze and learn how you do things in detail.
100% open source. Privacy always in mind.
Improve your workflow naturally.
Runs locally and fully transparent.
No black-box, no fees.
Made with <3 in Austria.
Quick Start
Clone the repo
git clone https://github.com/peab-dev/WorkTracker.git
cd WorkTracker
Run the installer
./install.sh
This sets up Python, dependencies, launchd services, and shell aliases.
Grant permissions in macOS settings
System Settings → Privacy & Security → enable Accessibility and Screen Recording for your Terminal app.
Check status
source ~/.zshrc # or: wtrl
wt status
The collector starts automatically. Data appears within seconds.
Level up your Work:Tracker output. Uncover unseen activity patterns & time-wastings with the power of AI. Connect local LLMs of your choice & improve your computer workflow in a new way.
Prompts for scheduled Claude Cowork tasks to give deep workflow insights will come in v0.0.2.
Requirements
| Requirement | Details |
|---|---|
| Operating System | macOS only (uses AppKit, Quartz, launchd) |
| Python | 3.9+ (installer can set up via Homebrew) |
| Xcode CLI Tools | Required for compiling Python dependencies |
| Disk Space | ~50 MB base + ~5 MB/day of snapshots |
Python Dependencies
| Package | Purpose |
|---|---|
pyobjc-framework-Cocoa | macOS app detection, clipboard |
pyobjc-framework-Quartz | Input monitoring (CGEventTap) |
pyobjc-framework-EventKit | Calendar integration |
pandas | Snapshot aggregation |
rapidfuzz | Fuzzy title matching |
pyyaml | Configuration files |
flask | Web dashboard |
Installation
The installer handles everything automatically:
./install.sh
What it does
- Checks prerequisites (macOS, Xcode CLI Tools, Python 3)
- Offers to install Homebrew and Python if missing
- Copies project to
~/WorkTracker - Creates directory structure (
data/,logs/,summaries/) - Sets up Python virtual environment + installs dependencies
- Generates 4 launchd plist files
- Installs and starts launchd services
- Adds shell aliases (
wts,wtl,wtr, etc.) to your shell config
macOS Permissions
| Permission | Why | Path |
|---|---|---|
| Accessibility | Keyboard/mouse event monitoring via CGEventTap | System Settings → Privacy & Security → Accessibility |
| Screen Recording | Reading window titles of other applications | System Settings → Privacy & Security → Screen Recording |
Grant access to your Terminal app (Terminal.app, iTerm2, Warp, etc.).
Uninstall
./uninstall.sh
Prompts to confirm, then:
- Stops and removes all launchd services
- Removes shell aliases from your shell config
- Optionally deletes collected data (or keeps it)
CLI Commands
All commands are run via the wt CLI tool:
wt <command>
Overview
| Command | Alias | Description |
|---|---|---|
wt status | wt s / wts | Show services, data counts, and latest reports |
wt tail | wtl | Follow collector logs live |
wt help | wt h | Show interactive help screen |
Collector
| Command | Alias | Description |
|---|---|---|
wt restart | wt r / wtr | Restart collector daemon |
wt start | — | Start collector |
wt stop | — | Stop collector |
Aggregator
| Command | Alias | Description |
|---|---|---|
wt daily | wt d / wtd | Run daily aggregation now |
wt weekly | wt w / wtw | Run weekly aggregation now |
wt monthly | wt m / wtm | Run monthly aggregation now |
Dashboards
| Command | Alias | Description |
|---|---|---|
wt dash | — | Open terminal dashboard (curses UI, 2s refresh) |
wt web | — | Start web dashboard at http://127.0.0.1:7880 |
wt menubar | wt mb / wtmb | Start macOS menubar widget (auto-starts web dashboard) |
wt docs | wt docu / wtdocs | Open documentation in browser |
Shell Aliases
Installed automatically by install.sh into your shell config:
| Alias | Expands to |
|---|---|
wts | wt status |
wtl | wt tail |
wtr | wt restart |
wtd | wt daily |
wtw | wt weekly |
wtm | wt monthly |
wtmb | wt menubar |
wtdocs | wt docs |
wtrl | exec $SHELL -l (reload shell) |
Dashboards
Terminal Dashboard
Run wt dash for a real-time curses-based UI showing:
- Service status (running / ready / not loaded)
- Current app + window title + input rates
- Today's stats: active time, sessions, focus count, switches
- Project breakdown with intensity bars
- Recent session timeline
- Latest report files
Web Dashboard
Run wt web to start a Flask server on http://127.0.0.1:7880:
- Same data as terminal dashboard, rendered in a browser
- Hourly activity chart
- Project distribution
- Top apps by time
- Session timeline
- Service and log monitoring
Reports
Reports are Markdown files generated by the aggregator. They serve as input for AI-powered summaries (e.g. via Claude Cowork).
Daily Report
Generated at 22:00 daily → summaries/daily/YYYY-MM-DD.md
- Overview: Active time, time range, focus sessions, app switches, idle ratio
- Project Distribution: Time, share, sessions, avg session, intensity
- App Usage: Top apps by time
- Timeline: Full session-by-session table
- Parallel Activities: Media playing during work
- Input Analysis: Keystroke peak hours, hourly intensity bars
- Git Activity: Commits per repo (if enabled)
- Calendar & Time Classification: Meetings vs. deep work (if enabled)
- Previous Day Comparison: Delta metrics
Weekly Report
Generated Sunday 23:00 → summaries/weekly/YYYY-Wnn.md
- Aggregated stats for the full week (Mon–Sun)
- Daily comparison table
- Weekly project distribution
- Trends: most/least productive day
- Previous week comparison
Monthly Report
Generated 1st of month 00:30 → summaries/monthly/YYYY-MM.md
- Full month aggregation
- Weekly comparison table
- Monthly project distribution
- Long-term trend (1st half vs. 2nd half)
- Previous month comparison
Configuration
All configuration lives in a single file:
~/WorkTracker/daemon/config.yaml
Collector Settings
| Key | Default | Description |
|---|---|---|
interval_seconds | 10 | Snapshot interval in seconds |
track_clipboard_content | true | Capture full clipboard text |
track_input_counts | true | Count keystrokes, clicks, scrolls |
track_media | true | Detect media playback (Spotify, YouTube, etc.) |
track_all_windows | true | Monitor all visible windows |
track_keystroke_content | false | Capture typed plaintext (privacy warning!) |
track_browser_urls | true | Read URLs from browser history DBs |
track_git | false | Track git commits and branches |
git_repos | [] | List of repo paths to monitor |
git_scan_interval_seconds | 60 | Git rescan interval |
track_calendar | false | Read events from macOS Calendar |
Distraction Notifications
| Key | Default | Description |
|---|---|---|
notifications.enabled | false | Enable macOS distraction warnings |
notifications.threshold_minutes | 15 | Minutes in distraction category before alert |
notifications.cooldown_minutes | 30 | Cooldown between alerts |
notifications.distraction_categories | ["Social Media", "Media/Entertainment"] | Categories that trigger warnings |
Aggregator Settings
| Key | Default | Description |
|---|---|---|
idle_threshold_seconds | 120 | Gap >2min starts a new session |
focus_session_min_seconds | 1500 | Sessions >25min counted as “focus” |
fuzzy_match_threshold | 0.7 | Levenshtein ratio for title matching |
same_app_grace_period_seconds | 30 | Same-app gap <30s = merge (tab switching) |
min_session_snapshots | 2 | Sessions with <2 snapshots get absorbed |
deep_work_min_minutes | 60 | Sessions >60min classified as deep work |
Project Patterns
Projects are matched by window title and URL patterns in:
~/WorkTracker/daemon/project_patterns.yaml
Example
projects:
easyAMS:
patterns: ["*easyams*", "*easyAMS*"]
category: "Development"
AI Research:
patterns: ["*claude*", "*openai*", "*chatgpt*"]
category: "AI/Research"
Crypto:
patterns: ["*binance*", "*bitcoin*"]
url_patterns: ["*binance.com*", "*coingecko.com*"]
category: "Crypto"
default_project: "Other"
Patterns use glob syntax (* = wildcard) and are matched case-insensitively against window titles. URL patterns are checked when track_browser_urls is enabled.
How It Works
WorkTracker follows a 3-layer architecture:
Data Flow
1. Snapshots (Collector → JSONL)
Every 10 seconds, the collector writes one JSON object to today's snapshot file. Each snapshot contains:
- Active app (name, bundle ID, window title)
- All visible windows
- Input activity (keystrokes, clicks, scroll, idle times)
- Clipboard content
- Browser URL (if applicable)
- Media playback state
- Git status (if enabled)
- Calendar events (if enabled)
- Sleep/wake events
2. Sessions (Aggregator → JSON)
The aggregator groups consecutive snapshots into sessions based on app continuity and idle gaps. Each session has: start/end times, duration, app, project, intensity score, input totals.
3. Reports (Aggregator → Markdown)
Statistics are computed from sessions and rendered into Markdown tables. These serve as structured input for AI-powered narrative summaries.
Directory Structure
Launchd Services
| Service | Label | Schedule | Behavior |
|---|---|---|---|
| Collector | com.peab.worktracker.collector |
Always on | RunAtLoad + KeepAlive (auto-restarts) |
| Daily Agg | com.peab.worktracker.aggregator.daily |
22:00 daily | Runs once, generates daily report |
| Weekly Agg | com.peab.worktracker.aggregator.weekly |
Sunday 23:00 | Runs once, generates weekly report |
| Monthly Agg | com.peab.worktracker.aggregator.monthly |
1st of month 00:30 | Runs once, generates monthly report |
All services run with Nice: 10 (low priority) and log to ~/WorkTracker/logs/.
Collector Deep Dive
The collector is a long-running Python daemon that takes a system snapshot every 10 seconds.
What It Captures
| Category | Data |
|---|---|
| Active App | Name, bundle ID, window title (via Accessibility API for true focus detection) |
| Windows | All visible windows with owner, title, position, layer |
| Input | Keystroke count, left/right clicks, scroll events, mouse distance, idle times |
| Clipboard | Full text content, change detection |
| Browser | Current URL from Safari/Chrome/Firefox/Arc history databases |
| Media | Playing status from Spotify, Apple Music, VLC, YouTube, Netflix, etc. |
| Git | Active branch, recent commits (optional) |
| Calendar | Current/upcoming events from macOS Calendar (optional) |
| System | Sleep/wake events, active display space |
Input Monitoring
Uses CGEventTap in listen-only mode (cannot inject events). Runs on a separate thread with a CFRunLoop. Tracks:
- Keystroke count (and optionally plaintext content)
- Mouse clicks (left, right)
- Scroll events
- Mouse travel distance (pixels)
Focus Detection
Uses the Accessibility API (AXUIElementCopyAttributeValue) instead of NSWorkspace.frontmostApplication. This correctly identifies the focused app even when floating overlays (like the Claude Cowork widget) hold frontmost status.
Aggregator Deep Dive
The aggregator transforms raw snapshots into structured sessions and Markdown reports.
Execution Modes
python aggregator.py --mode daily # today's report
python aggregator.py --mode daily --date 2026-04-01 # specific date
python aggregator.py --mode weekly
python aggregator.py --mode monthly
Processing Pipeline
- Load JSONL snapshots into a Pandas DataFrame
- Flatten nested fields (active_app, input, clipboard, system, media, git)
- Detect sessions using two-tier merging (see below)
- Match each session to a project via pattern rules
- Calculate statistics (time distribution, intensity, focus sessions)
- Render Markdown report with tables
- Run pattern learning for "Other" sessions
Session Detection
Sessions group consecutive snapshots that represent a single work context.
Two-Tier Merging
Tier 1: Same App + Small Gap (<30s)
If the same app appears within 30 seconds, snapshots are always merged. This handles rapid tab switching within browsers or editors.
Tier 2: Same App + Larger Gap
If the gap is larger but the app is the same, fuzzy title matching (Levenshtein ratio ≥0.7) determines whether to merge.
Session Split Triggers
- Different app detected
- Idle gap exceeds
idle_threshold_seconds(default: 120s) - Sleep/wake event detected
- Title similarity drops below threshold
Micro-Session Absorption
Sessions with fewer than min_session_snapshots (default: 2) are absorbed into the preceding session to reduce noise.
Pattern Learning
WorkTracker automatically learns new project patterns from unclassified ("Other") sessions.
How It Works
- After daily aggregation, "Other" sessions are analyzed
- Window titles and URLs are grouped by keyword/domain
- Groups with ≥5 minutes total time become pattern suggestions
- Suggestions are written to
learned_patterns.yaml
Priority
Static patterns (project_patterns.yaml) always take precedence over learned patterns. This ensures hand-curated rules are never overridden.
Learned Pattern Format
projects:
github.com:
patterns: ["*github.com*"]
url_patterns: ["*github.com*"]
category: "auto-learned"
_auto_generated: true
_total_time_seconds: 3240
_first_seen: "2026-04-01"
_sample_titles: ["Issues - anthropics/claude-code"]
Special Features
Sleep/Wake Detection
Two mechanisms ensure sessions are correctly split across sleep events:
- Explicit: NSWorkspace notifications for macOS sleep/wake
- Heuristic: If a loop iteration takes >3× the expected interval, a gap record is inserted
Midnight Continuity
The daily aggregator loads the tail end of the previous day's snapshots (≥23:55) to handle sessions spanning midnight correctly.
Intensity Scoring
Each session gets an intensity score based on normalized input activity (keystrokes + clicks + scrolls relative to session duration). Displayed as 10-character bar charts in reports and dashboards.
Focus Sessions
Sessions longer than 25 minutes (configurable via focus_session_min_seconds) are tracked separately as focus sessions. Sessions >60 minutes are classified as deep work.
Browser URL Tracking
The collector reads browser history databases directly (SQLite) for Safari, Chrome, Firefox, Brave, Arc, and Edge. URLs are matched against url_patterns in project patterns for accurate classification.
Media Detection
Detects playback from Spotify, Apple Music, VLC, IINA, Podcasts (native apps) and YouTube, Netflix, Twitch, SoundCloud (browser tabs via title pattern matching). Parallel media activity is tracked alongside work sessions.