# Tracker (Windows) A single PowerShell script that watches which programs you use and writes one JSON file per day to `Documents\ActivityTracker\`, named `-YYYY-MM-DD.json`. Open the resulting files in the [dashboard](../index.html) to see charts. It is plain text on purpose. Open `ActivityTracker.ps1` in Notepad; the comment block at the top explains everything it does, and the code below it is short enough to read in ten minutes. ## Run it | File | What it does | |------|--------------| | `Start-Tracker.bat` | Starts the tracker in a minimized PowerShell window. Leave it running. | | `Stop-Tracker.bat` | Asks the tracker to save and exit. It notices within about half a second. | | `Test-Tracker.bat` | Takes one sample and prints what the tracker sees: focused window, open windows, idle time, any meeting detected. Saves nothing. Run this first. | You do not have to stop the tracker to see your day: it rewrites today's file every 60 seconds, so the JSON is already in `Documents\ActivityTracker\` while it runs, and you can drop that file on the dashboard at any point. Stopping with `Stop-Tracker.bat` (or Ctrl+C in its window) writes the final partial minute before exiting. Closing the window with the X, shutting down, or losing power skips that last write, so you lose at most the seconds since the last save. The file itself is never damaged: each save is written to a `.tmp` file and renamed over the real one, so it is always either the previous complete file or the new complete file. Starting and stopping several times in one day is fine. On each start the tracker reads today's file and carries on from it, so totals keep adding up instead of resetting or double counting. Each off period simply shows as a gap in the dashboard's timeline. The tracker prints a one-line status every minute when it saves, e.g. ``` 10:31:05 active 142m attending 27m idle 9m | active AutoCAD - Autodesk AutoCAD 2025 - [Site-Plan-A.dwg] ``` ### Why the `.bat` files say `-ExecutionPolicy Bypass` Windows refuses to run PowerShell *script files* that aren't digitally signed unless you say so. That flag says so, for that one window only. It doesn't elevate anything, doesn't change a system setting, and doesn't survive the window closing. The alternative would be shipping a compiled `.exe`, which you could not read. ### Start at logon (optional) Task Scheduler → Create Basic Task → trigger *When I log on* → action *Start a program* → browse to `Start-Tracker.bat`. Tick *Run only when user is logged on*. Untick *Stop the task if it runs longer than…* under Settings. ## Settings Copy `config.example.json` to `config.json` in the same folder and edit. Every key is optional. | Key | Default | Meaning | |-----|---------|---------| | `pollIntervalSec` | `5` | How often to look at the screen. | | `idleThresholdSec` | `300` | No keyboard/mouse input for this long counts as idle (or *attending*, if a meeting is on). | | `saveIntervalSec` | `60` | How often to rewrite today's JSON file. | | `outputFolder` | `Documents\ActivityTracker` | Where the daily files go. Use `\\` in the path. | | `trackerId` | random | Prefix for the file names, e.g. `a7f3c2d1-2026-09-01.json`, so files from several people can share a folder without overwriting each other. Blank means an 8-character random id is generated on first run and saved to `tracker.id`. Set it to a label like `josiah-desktop` if you prefer. Not derived from your login or machine. | | `captureTitles` | `true` | `false` records app names only, with no window titles at all. Titles are the most personal thing in the file (email subjects, file names), so turn this off if you'd rather not have them. | | `ignoreProcesses` | shell helpers | Processes whose windows are never counted (Start menu, search box, etc.). | | `ignoreTitles` | `^Program Manager$` | Regexes for window titles to skip (the desktop itself). | | `meetingDetection.microphone` | `true` | Treat "an app has the microphone open" as being in a meeting. Read from the same place Windows gets its microphone-in-use icon. | | `meetingDetection.rules` | Teams, Zoom, Meet, Webex | A visible window whose process and title match a rule (and don't match `exclude`) is a meeting. Regexes. | ## What is and isn't collected **Collected:** process name (e.g. `acad`), the friendly name from the program (e.g. `AutoCAD`), window titles, seconds of focus / open / attending / idle, timestamps, and the random tracker id. See [`docs/schema.md`](../docs/schema.md) for the exact format. **Not collected:** keystrokes, mouse positions, clipboard, screenshots, URLs, file paths, document contents, user name, machine name, IP address. **Never sent anywhere.** There is no network code. Verify: ``` findstr /i "http invoke-webrequest invoke-restmethod webclient net.sockets" ActivityTracker.ps1 ``` The only hits are in the comment at the top that tells you to run this search. ## How detection works - **Focus**: `GetForegroundWindow` tells us which window is in front; its title bar text is read with `GetWindowText`. For AutoCAD that includes the drawing name; for Outlook the folder or message subject. - **Open**: `EnumWindows` lists every visible top-level window, so an app that is open but behind other windows still accrues *open* time. - **Idle**: `GetLastInputInfo` reports the time of your last key press or mouse movement. Nothing about *which* keys. - **Meeting**: either a visible window matches a rule (a Teams window ending in `| Microsoft Teams` that isn't one of the main tabs, `Zoom Meeting`, `Meet - …` in a browser), or the Windows registry says an app currently has the microphone open. Idle time while a meeting is on is recorded as *attending* and attributed to the meeting. ## Files it creates - `Documents\ActivityTracker\-YYYY-MM-DD.json`: one per day, rewritten every minute. - `tracker.id` next to the script: the random id, created on first run. - `tracker.pid` next to the script while it runs; `tracker.stop` briefly when you stop it. If you start the tracker again on the same day it reads the existing file and keeps counting rather than starting over.