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.

Runs Locally Open Source No Blackbox App Tracking Flow Insights

Quick Start

1

Clone the repo

git clone https://github.com/peab-dev/WorkTracker.git
cd WorkTracker
2

Run the installer

./install.sh

This sets up Python, dependencies, launchd services, and shell aliases.

3

Grant permissions in macOS settings

System Settings → Privacy & Security → enable Accessibility and Screen Recording for your Terminal app.

4

Check status

source ~/.zshrc   # or: wtrl
wt status

The collector starts automatically. Data appears within seconds.

Optional Power Up

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

RequirementDetails
Operating SystemmacOS only (uses AppKit, Quartz, launchd)
Python3.9+ (installer can set up via Homebrew)
Xcode CLI ToolsRequired for compiling Python dependencies
Disk Space~50 MB base + ~5 MB/day of snapshots

Python Dependencies

PackagePurpose
pyobjc-framework-CocoamacOS app detection, clipboard
pyobjc-framework-QuartzInput monitoring (CGEventTap)
pyobjc-framework-EventKitCalendar integration
pandasSnapshot aggregation
rapidfuzzFuzzy title matching
pyyamlConfiguration files
flaskWeb dashboard

Installation

The installer handles everything automatically:

./install.sh

What it does

  1. Checks prerequisites (macOS, Xcode CLI Tools, Python 3)
  2. Offers to install Homebrew and Python if missing
  3. Copies project to ~/WorkTracker
  4. Creates directory structure (data/, logs/, summaries/)
  5. Sets up Python virtual environment + installs dependencies
  6. Generates 4 launchd plist files
  7. Installs and starts launchd services
  8. Adds shell aliases (wts, wtl, wtr, etc.) to your shell config

macOS Permissions

Required: WorkTracker needs two macOS permissions to function correctly. Without them, input tracking and window title detection will fail silently.
PermissionWhyPath
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:

CLI Commands

All commands are run via the wt CLI tool:

wt <command>

Overview

CommandAliasDescription
wt statuswt s / wtsShow services, data counts, and latest reports
wt tailwtlFollow collector logs live
wt helpwt hShow interactive help screen

Collector

CommandAliasDescription
wt restartwt r / wtrRestart collector daemon
wt startStart collector
wt stopStop collector

Aggregator

CommandAliasDescription
wt dailywt d / wtdRun daily aggregation now
wt weeklywt w / wtwRun weekly aggregation now
wt monthlywt m / wtmRun monthly aggregation now

Dashboards

CommandAliasDescription
wt dashOpen terminal dashboard (curses UI, 2s refresh)
wt webStart web dashboard at http://127.0.0.1:7880
wt menubarwt mb / wtmbStart macOS menubar widget (auto-starts web dashboard)
wt docswt docu / wtdocsOpen documentation in browser

Shell Aliases

Installed automatically by install.sh into your shell config:

AliasExpands to
wtswt status
wtlwt tail
wtrwt restart
wtdwt daily
wtwwt weekly
wtmwt monthly
wtmbwt menubar
wtdocswt docs
wtrlexec $SHELL -l (reload shell)

Dashboards

Terminal Dashboard

Run wt dash for a real-time curses-based UI showing:

Web Dashboard

Run wt web to start a Flask server on http://127.0.0.1:7880:

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

Weekly Report

Generated Sunday 23:00summaries/weekly/YYYY-Wnn.md

Monthly Report

Generated 1st of month 00:30summaries/monthly/YYYY-MM.md

Configuration

All configuration lives in a single file:

~/WorkTracker/daemon/config.yaml

Collector Settings

KeyDefaultDescription
interval_seconds10Snapshot interval in seconds
track_clipboard_contenttrueCapture full clipboard text
track_input_countstrueCount keystrokes, clicks, scrolls
track_mediatrueDetect media playback (Spotify, YouTube, etc.)
track_all_windowstrueMonitor all visible windows
track_keystroke_contentfalseCapture typed plaintext (privacy warning!)
track_browser_urlstrueRead URLs from browser history DBs
track_gitfalseTrack git commits and branches
git_repos[]List of repo paths to monitor
git_scan_interval_seconds60Git rescan interval
track_calendarfalseRead events from macOS Calendar

Distraction Notifications

KeyDefaultDescription
notifications.enabledfalseEnable macOS distraction warnings
notifications.threshold_minutes15Minutes in distraction category before alert
notifications.cooldown_minutes30Cooldown between alerts
notifications.distraction_categories["Social Media", "Media/Entertainment"]Categories that trigger warnings

Aggregator Settings

KeyDefaultDescription
idle_threshold_seconds120Gap >2min starts a new session
focus_session_min_seconds1500Sessions >25min counted as “focus”
fuzzy_match_threshold0.7Levenshtein ratio for title matching
same_app_grace_period_seconds30Same-app gap <30s = merge (tab switching)
min_session_snapshots2Sessions with <2 snapshots get absorbed
deep_work_min_minutes60Sessions >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:

macOS (focus, windows, input, clipboard) | v [Collector] every 10s | v data/snapshots/YYYY-MM-DD.jsonl | v [Aggregator] daily / weekly / monthly | | v v data/sessions/ summaries/*.md | v [AI Summary] via Cowork

Data Flow

1. Snapshots (Collector → JSONL)

Every 10 seconds, the collector writes one JSON object to today's snapshot file. Each snapshot contains:

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

~/WorkTracker/ ├── wt # CLI tool ├── install.sh # Installer ├── uninstall.sh # Uninstaller │ ├── daemon/ │ ├── collector.py # Snapshot collector (10s loop) │ ├── aggregator.py # Session aggregator + reports │ ├── dashboard.py # Terminal dashboard (curses) │ ├── web_dashboard.py # Web dashboard (Flask) │ ├── ctl.sh # Alternative control script │ ├── config.yaml # Main configuration │ ├── project_patterns.yaml # Project matching rules │ ├── learned_patterns.yaml # Auto-learned patterns │ ├── requirements.txt # Python dependencies │ └── .venv/ # Python virtual environment │ ├── data/ │ ├── snapshots/ # YYYY-MM-DD.jsonl (1 per day) │ └── sessions/ # YYYY-MM-DD.json (1 per day) │ ├── logs/ # All log files │ ├── summaries/ │ ├── daily/ # YYYY-MM-DD.md │ ├── weekly/ # YYYY-Wnn.md │ └── monthly/ # YYYY-MM.md │ ├── launchd/ # Generated plist files └── docs/ # This documentation

Launchd Services

ServiceLabelScheduleBehavior
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

CategoryData
Active AppName, bundle ID, window title (via Accessibility API for true focus detection)
WindowsAll visible windows with owner, title, position, layer
InputKeystroke count, left/right clicks, scroll events, mouse distance, idle times
ClipboardFull text content, change detection
BrowserCurrent URL from Safari/Chrome/Firefox/Arc history databases
MediaPlaying status from Spotify, Apple Music, VLC, YouTube, Netflix, etc.
GitActive branch, recent commits (optional)
CalendarCurrent/upcoming events from macOS Calendar (optional)
SystemSleep/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:

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

  1. Load JSONL snapshots into a Pandas DataFrame
  2. Flatten nested fields (active_app, input, clipboard, system, media, git)
  3. Detect sessions using two-tier merging (see below)
  4. Match each session to a project via pattern rules
  5. Calculate statistics (time distribution, intensity, focus sessions)
  6. Render Markdown report with tables
  7. 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

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

  1. After daily aggregation, "Other" sessions are analyzed
  2. Window titles and URLs are grouped by keyword/domain
  3. Groups with ≥5 minutes total time become pattern suggestions
  4. 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:

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.

Copied